Category: CMS (Joomla!, Moodle, Wordpress)

  • WordPress Content Security Policy (2)

    WordPress Content Security Policy (2)

    ครั้งก่อนนำเสนอปลั๊กอิน CSP-ANTS&STP ซึ่งปัจจุบันได้เปลี่ยนชื่อเป็น CSP Friendly Security แต่ถึงจะมีการปรับปรุงแล้วก็อาจเกิด Header Overflow ได้ในอนาคต ดูผลลัพธ์ของ CSP ที่ได้เป็นดังภาพ

    ซึ่งหลังจากคุยกับ Gemini ก็ได้รับคำแนะนำให้เปลี่ยน เนื่องจากมีโอกาสเกิด Header Overflow ได้ (ถึงแม้จะมีขนาดใหญ่มาก 8KB ถึง 16KB) ก็เลยให้เขียนใหม่ปรับปรุงจากรุ่นเดิมของ CSP Friendly Security โดยใช้ z.ai และ gemini.google.com ปรับปรุง

    สิ่งที่ต้องมี

    1. Woody snippets
    2. สร้าง new php snippets ใส่โค้ดต่อไปนี้แล้วเปลี่ยนเป็น run everywhere แล้วกดบันทึกให้เรียบร้อย
    CSP {PHP}
    add_action('template_redirect', 'csp_controller');
    
    function csp_controller() {
        // ตรวจสอบว่าเป็นหน้า Admin หรือหน้าเป้าหมายหรือไม่
        $target_pages = array('cd-key');
        if (is_admin() || is_page($target_pages)) {
            // ส่ง Simple Header แล้วจบการทำงานทันที (ไม่เปิด Buffer)
            if (!headers_sent()) {
                header("Content-Security-Policy: upgrade-insecure-requests");
            }
            return;
        }
    
        if (function_exists('litespeed_autoload')) {
            add_filter('litespeed_buffer_after', 'process_csp_buffer', 0);
        } else {
            ob_start('process_csp_buffer');
        }
    }
    
    function process_csp_buffer($content) {
        // ตรวจสอบว่าเป็นหน้า Admin หรือหน้าเป้าหมายหรือไม่
        $target_pages = array('cd-key');
        if (is_admin() || is_page($target_pages)) {
            return $content;
        }
    
        // สร้าง Nonce
        $nonce = base64_encode(random_bytes(16));
    
        // แทรก Nonce (ใช้รหัส Hex เพื่อหลบ Editor บั๊ก)
        $pattern = '#<(script|style)(?![^>]*\bnonce=)(?![^>]*\btype=[\x22\x27]application/(ld\+json|json)[\x22\x27])([^>]*)>#i';
        $content = preg_replace($pattern, "<$1 nonce='" . $nonce . "'$3>", $content);
    
        $sha256_csp = get_event_hashes($content);
        $uris = get_allowed_domains($content);
        $uri_string = implode(' ', $uris);
    
        $script_src = "script-src https: 'strict-dynamic' 'nonce-" . $nonce . "'";
        if (!empty($sha256_csp)) {
            $script_src .= " " . $sha256_csp;
        }
    
        // สร้าง Full CSP Header
        $csp_header = sprintf(
            "Content-Security-Policy: base-uri 'self' %s data:; object-src 'none'; %s; frame-ancestors 'none';",
            $uri_string,
            $script_src
        );
    
        if (!headers_sent()) {
            header($csp_header);
        }
    
        return $content;
    }
    
    // --- ฟังก์ชัน Helper (คงเดิม) ---
    
    function get_event_hashes($output) {
        $sha256 = array();
        if (preg_match_all('#\s(onload|onclick)\s*=\s*([\x22\x27])(.+?)\2#is', $output, $matches)) {
            foreach ($matches[3] as $event_code) {
                if (!empty($event_code)) {
                    $sha256[] = base64_encode(hash('sha256', $event_code, true));
                }
            }
        }
        if (class_exists('autoptimizeConfig')) {
            $sha256[] = base64_encode(hash('sha256', "this.onload=null;this.media='all';", true));
        }
        if (empty($sha256)) return '';
        $unique_hashes = array_unique($sha256);
        $formatted_hashes = array();
        foreach ($unique_hashes as $h) {
            $formatted_hashes[] = "'sha256-" . $h . "'";
        }
        return "'unsafe-hashes' " . implode(' ', $formatted_hashes);
    }
    
    function get_allowed_domains($string) {
        $result = array();
        $domains = array(
            'https://secure.gravatar.com/avatar/',
            'https://fonts.googleapis.com/',
            'https://maxcdn.bootstrapcdn.com/',
            'https://cdn.jsdelivr.net/'
        );
        foreach ($domains as $domain) {
            if (strpos($string, $domain) !== false) {
                $result[] = $domain;
            }
        }
        return $result;
    }
    1. เมื่อไปทดสอบที่ https://securityheaders.com/
    2. ก็จะได้ CSP ใหม่ที่สั้นลง
    content-security-policybase-uri ‘self’ data:; object-src ‘none’; script-src https: ‘strict-dynamic’ ‘nonce-sdfadsdfasdfadsfadsfa==’; frame-ancestors ‘none’;
    1. ก็ยังมี recomendation ที่ต้องปรับปรุงอีกนิดหน่อยค่อยๆ ทำไป
    2. จบ… ขอให้สนุก
  • Subresource Integrity

    Subresource Integrity

    เมื่อต้องการเพิ่มคะแนนของ HTTP Observatory Report เลื่อนไปๆ จะเจอว่าคะแนนของ Subresource integrity (SRI) ยังเป็น -5 ทำไงได้บ้างสำหรับ WordPress


    SRI คืออะไร

    SRI หรือ Subresource Integrity คือฟีเจอร์ด้านความปลอดภัยที่ช่วยให้ Browser ตรวจสอบว่าไฟล์ที่ถูกดึงมาจากภายนอก (เช่น JS หรือ CSS จาก CDN) ไม่ถูกแก้ไขหรือแอบฝังมัลแวร์มาโดยผู้ไม่หวังดี

    สิ่งที่ต้องมี

    1. ปลักอิน Woody snippets
    2. code สร้าง SRI ซึ่ง code นี้ได้จาก ai 2 ตัวช่วยกันคิด (กรั่ก ๆ) คือ z.ai และ gemini.google.com
    PHP
    // 1. Hook เพื่อแทรก SRI ให้ Script และ Style
    add_filter( 'script_loader_tag', 'auto_add_sri', 10, 2 );
    add_filter( 'style_loader_tag', 'auto_add_sri', 10, 2 );
    
    function auto_add_sri( $tag, $handle ) {
        if ( ! preg_match( '/\s(src|href)=[\x27\x22]([^\x27\x22]+)[\x27\x22]/i', $tag, $matches ) ) {
            return $tag;
        }
        
        $attr = $matches[1]; 
        $url  = $matches[2];
    
        $site_url = site_url();
        if ( strpos( $url, 'http' ) !== 0 || strpos( $url, $site_url ) === 0 ) {
            return $tag;
        }
    
        $hash_key = 'sri_hash_' . md5( $url );
        $hash = get_transient( $hash_key );
    
        if ( false === $hash ) {
            $response = wp_remote_get( $url, array( 'timeout' => 5 ) ); 
            
            if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) != 200 ) {
                set_transient( $hash_key, 'failed', HOUR_IN_SECONDS );
                return $tag;
            }
    
            $content = wp_remote_retrieve_body( $response );
            $hash = 'sha384-' . base64_encode( hash( 'sha384', $content, true ) );
            
            set_transient( $hash_key, $hash, 12 * HOUR_IN_SECONDS );
        }
    
        if ( $hash && $hash !== 'failed' ) {
            $replace_target = ' ' . $attr . '=';
            $replace_with = ' integrity="' . $hash . '" crossorigin="anonymous" ' . $attr . '=';
            return str_replace( $replace_target, $replace_with, $tag );
        }
    
        return $tag;
    }
    1. เปิดใช้งาน code ให้เป็น run everywhere
    2. ลองไปทดสอบที่เว็บ https://developer.mozilla.org/en-US/observatory
    3. ได้ผลดังภาพ
    1. ยังมีคะแนนที่ต้องมีการปรับตาม recomendation เพิ่มเติม
    2. สคริปต์นี้จะเก็บ cache ของ integrity ของสคริปต์ภายนอกไว้ 12 ชั่วโมง ถ้ามีผู้ใช้เข้าเว็บในช่วงเวลานี้แล้ว สคริปต์ไม่มีการเปลี่ยนแปลงก็จะไม่มีปัญหา แต่ถ้ามีการเปลี่ยนแปลงแล้วค่า Hash ไม่ตรงก็จะโดนบล็อคทันที ซึ่งต้องดูว่าเวลาเท่าไหร่จึงจะเหมาะสม
    3. จบ…ขอให้สนุก
  • WordPress Content Security Policy

    WordPress Content Security Policy

    1. CSP สำหรับ WordPress ค่อนข้างยุ่งยาก ปรับโน่นนิดปิดนี่หน่อย เว็บเพี้ยน
    2. เมื่อลองค้นไป ก็เจอปลักอินอยู่ตัวนึง ที่ช่วยสร้าง CSP ให้อัตโนมัติ นั่นคือ CSP-ANTS&STP แต่เนื่องจากปลักอินตัวนี้ไม่ได้รับการปรับปรุงมาตั้งแต่ 2022 ซึ่งนานเกินไป แล้ว แต่จริงๆ มันยังใช้ได้อยู่
    3. แต่ปลั๊กอินก็ยังมีบักเมื่อไปตรวจกับเว็บ https://securityheaders.com แล้วจะมีข้อความเตือนดังภาพ
    1. เพื่อแก้ปัญหานี้เลยต้องเขียนปลักอินตัวนี้ลงในปลักอินอีกตัวที่ชื่อ Woody snippets
    2. คลิก Add ใน Woody snippets เพิ่มข้อความต่อไปนี้ ส่วนนี้ได้แก้ไข Error บน https://securityheaders.com แล้วด้วย
    /**
    * Plugin Name:       CSP-ANTS&ST
    * Description:       Add a nonce to each script and style tags, and set those nonces in CSP header
    * Version:           1.1.1
    * Requires at least: 5.9
    * Requires PHP:      7.3
    * Author:            Pascal CESCATO
    * Author URI:        https://pascalcescato.gdn/
    * License:           GPL v2 or later
    * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
    */
    
    add_action('send_headers', function () {
    if (is_admin()){
        header("Content-Security-Policy: upgrade-insecure-requests");
        return;
    } 
    //เลือก page ที่ไม่ต้องการให้ CSP ทำงาน
    if (is_page(['cd-key'])){
        header("Content-Security-Policy: upgrade-insecure-requests");
        return;
    } 
    
    if (function_exists('litespeed_autoload')):
    // lscache version
    function cspantsst_cspantsst_lscwp_check ( $content ) {
    
    $uris = implode ( ' ', cspantsst_search_for_sources ( $content ) );
    
    $sha256_csp = cspantsst_search_for_events ( $content );
    
    $nonces = [];
    
    $content = preg_replace_callback ( '#<script.*?\>#', function ( $matches ) use ( &$nonces ) {
    $nonce = wp_create_nonce ( $matches[ 0 ] );
    $nonces[] = $nonce;
    
    return str_replace ( '<script', "<script nonce='{$nonce}'", $matches[ 0 ] );
    }, $content );
    
    $content = preg_replace_callback ( '#<style.*?\>#', function ( $matches ) use ( &$nonces ) {
    $nonce = wp_create_nonce ( $matches[ 0 ] );
    $nonces[] = $nonce;
    
    return str_replace ( '<style', "<style nonce='{$nonce}'", $matches[ 0 ] );
    }, $content );
    
    $nonces_csp = array_reduce ( $nonces, function ( $header, $nonce ) {
    return "{$header} 'nonce-{$nonce}'";
    }, '' );
    
    header ( sprintf ( "Content-Security-Policy:  base-uri 'self' %1s data:; object-src 'none'; script-src https:%2s %3s 'strict-dynamic'; frame-ancestors 'none'; ", $uris, $nonces_csp, $sha256_csp ));
    return $content;
    }
    
    add_filter ( 'litespeed_buffer_after', 'cspantsst_cspantsst_lscwp_check', 0 );
    
    else:
    // otherwise
    add_action ( 'template_redirect', function () {
    
    ob_start ( function ( $output ) {
    
    $uris = implode ( ' ', cspantsst_search_for_sources ( $output ) );
    
    $sha256_csp = cspantsst_search_for_events ( $output );
    
    $nonces = [];
    
    $output = preg_replace_callback ( '#<script.*?\>#', function ( $matches ) use ( &$nonces ) {
    $nonce = wp_create_nonce ( $matches[ 0 ] );
    $nonces[] = $nonce;
    return str_replace ( '<script', "<script nonce='{$nonce}'", $matches[ 0 ] );
    }, $output );
    
    $output = preg_replace_callback ( '#<style.*?\>#', function ( $matches ) use ( &$nonces ) {
    $nonce = wp_create_nonce ( $matches[ 0 ] );
    $nonces[] = $nonce;
    return str_replace ( '<style', "<style nonce='{$nonce}'", $matches[ 0 ] );
    }, $output );
    
    $header = '';
    $nonces_csp = array_reduce ( $nonces, function ( $header, $nonce ) {
    return "{$header} 'nonce-{$nonce}'";
    }, '' );
    
    header ( sprintf ( "Content-Security-Policy: base-uri 'self' %1s data:; object-src 'none'; script-src https:%2s %3s 'strict-dynamic'; frame-ancestors 'none';", $uris, $nonces_csp, $sha256_csp ) );
    return $output;
    } );
    } );
    endif;
    
    function cspantsst_search_for_events ( $output ) {
    
    $sha256 = array ();
    
    preg_match_all ( '/onload="(?<onload>[^"]+)"|onclick="(?<onclick>[^"]+)"/', $output, $matches );
    foreach ( $matches[ 'onload' ] as $match ):
    if ( !empty ( $match ) )
    $sha256[] = base64_encode ( hash ( 'sha256', $match, true ) );
    endforeach;
    foreach ( $matches[ 'onclick' ] as $match ):
    if ( !empty ( $match ) )
    $sha256[] = base64_encode ( hash ( 'sha256', $match, true ) );
    endforeach;
    
    if ( class_exists ( 'autoptimizeConfig' ) ):
    $sha256[] = base64_encode ( hash ( 'sha256', "this.onload=null;this.media='all';", true ) );
    endif;
    
    
    $header_sha256 = "'unsafe-hashes'";
    $sha256_csp = array_reduce ( $sha256, function ( $header, $sha256_item ) {
    return "{$header} 'sha256-{$sha256_item}'";
    }, '' );
    
    if ( !empty ( $sha256_csp ) )
    $sha256_csp = $header_sha256 . $sha256_csp;
    
    return $sha256_csp;
    }
    
    function cspantsst_search_for_sources ( $string ) {
    
    $result = array ();
    if ( strpos ( $string, 'https://secure.gravatar.com/avatar/' ) ):
    $result[] = 'https://secure.gravatar.com/avatar/';
    endif;
    if ( strpos ( $string, 'https://fonts.googleapis.com/' ) ):
    $result[] = 'https://fonts.googleapis.com/';
    endif;
    if ( strpos ( $string, 'https://maxcdn.bootstrapcdn.com/' ) ):
    $result[] = 'https://maxcdn.bootstrapcdn.com/';
    endif;
    return $result;
    
    }
    });
    1. และใน code นี้มีส่วนของโค้ดที่ใช้ระบุหน้าที่ไม่ต้องการให้ CSP-ANTS&STP ทำงานด้วย เนื่องจากการกำหนด CSP บางครั้งอาจทำให้เว็บไม่สามารถทำงานได้อย่างที่ต้องการ ต้องปรับลดความปลอดภัยลงให้เหลือ upgrade-insecure-requests แทนโค้ดดังกล่าวคือ
    if (is_page(['cd-key'])){
        header("Content-Security-Policy: upgrade-insecure-requests");
        return;
    } 
    
    1. แล้วเลื่อนลงมาในส่วนของ Base options เลือกเป็น Run everywhere
    1. กด Save
    2. และมาตรวจสอบในหน้า Woody snippets ว่าเปิดใช้งานดังภาพแล้วหรือยัง
    1. ทดสอบด้วยการไปเช็คด้วย https://securityheaders.com/ ก็จะได้ A+ เย่..
    1. แถม https://developer.mozilla.org/en-US/observatory/ อีกเว็บนึง เมื่อติดตั้งโค้ดของ CSP ก็สามารถทำคะแนนที่ดีขึ้นทันตาเห็น
    1. สุดท้ายก็สามารถ ถอนปลักอิน CSP-ANTS&STP ออกจากเว็บไซต์ได้เลย ต้องขอบคุณคนเขียนปลักอินตัวนี้จากใจจริง
    2. จบ.
    3. ขอให้สนุก..
  • การกำหนดค่าพื้นฐานความปลอดภัยสำหรับ IIS และ WordPress บน Windows Server

    เพื่อให้ Web Server ของเราปลอดภัยจากการถูกโจมตี บทความนี้จะเป็นการแนะนำการกำหนดค่าต่างๆของ web server ที่ให้บริการ ซึ่งทำงานด้วยบน Windows Server และ มีการติดตั้ง IIS, PHP, MySql, ASP.Net และ WordPress

    • การกำหนดส่วนของ Windows Server อ้างอิงคำแนะนำจาก Quays SSL Labs ให้ได้ระดับ A ขึ้นไป
      1. ใช้ใบรับรองจาก CA ที่น่าเชื่อถือ และ ใช้การ RSA 2048 bits (SHA256withRSA) ขึ้นไป
      2. การกำหนด Cipher Suites ที่ปลอดภัย ซึ่งจะมีเครื่องมือที่ช่วยในการกำหนดดังนี้
        • IIS Crypto เป็นโปรแกรมฟรีไม่ต้องติดตั้งสำหรับช่วยจัดการกำหนด protocols, ciphers, hashes and key exchange algorithms บน Windows Server โดยกำหนดพื้นฐานดังนี้
          1. เมนู Schannel
          1.1 Protocols เลือกกำหนดใช้งาน TLS 1.2 และ/หรือ TLS 1.3 เท่านั้น
          1.2 Cipher เลือกกำหนดเป็น AES
          1.3 Hashes เลือก SHA 256 ขึ้นไป
          1.4 Key Exchanges สามารถเลือกได้ทั้ง Diffie-Hellman, PKCS และ ECDH
          2. เมนู Cipher Suites สามารถกำหนด Cipher Suites ที่ปลอดภัยในปัจจุบัน ซึ่งค้นหาได้จากเว็บ https://www.tenable.com/plugins/nessus/156899
      3. เป็นส่วนของการกำหนดใน IIS
        • การกำหนดสำหรับ Security Headers ให้ได้ระดับ A+ อ้างอิงคำแนะนำจากเว็บ https://securityheaders.com/
        • การจัดการ Http Response Header โดยกำหนดค่าดังนี้
          1. X-Frame-Options เป็นการกำหนดเพื่อหลีกเลี่ยงจากการถูกโจมตีด้วย Clickjacking
            ตัวอย่างการกำหนดค่าเป็น SAMEORIGIN
          2. X-XSS-Protection เป็นการป้องกันการโหลดสคริปต์ข้ามไซต์
            ตัวอย่างการกำหนดเป็น 1; mode=block
          3. X-Content-Type-Options เป็นการป้องการโจมตีเนื้อหาประเภท MINE (Multipurpose Internet Mail Extensions) ซึ่งเป็นรูปแบบที่ใช้ระบุประเภทของข้อมูลที่ถูกส่งผ่านเครือข่าย หรือเก็บที่เครื่องอุปกรณ์ มันช่วยบอกให้ระบบรับรู้ว่าไฟล์เป็นประเภทไหนและวิธีการจัดการข้อมูลนั้น ที่อาจถูกใช้ในการโจมตีเพื่อหลอกลวงระบบหรือละเว้นมาตรฐานการตรวจสอบปลอดภัย เช่น application/pdf, image/jpeg, text/html
            ตัวอย่างการกำหนดค่าเป็น nosniff
          4. Referrer-Policy เป็นการควบคุมการส่งผ่านส่วนอ้างอิง เช่น ป้องกันส่วน HTTPS ไม่ให้กลับไป HTTP ที่ไม่ปลอดภัย
            ตัวอย่างการกำหนดค่าเป็น no-referrer-when-downgrade
          5. Strict-Transport-Security เป็นการช่วยให้การเข้าเว็บไซต์ด้วย HTTPS เท่านั้น
            ตัวอย่างการกำหนดค่าเป็น max-age=31536000; includeSubDomains; preload
          6. Content-Security-Policy เป็นการระบุที่มาของเนื้อหาที่ได้รับอนุญาตให้โหลดบนเว็บไซต์ เช่น JavaScript เพื่อป้องกันการโจมตีแบบ Cross-Site Scripting (XSS)
            ตัวอย่างการกำหนดค่าเป็น upgrade-insecure-requests
          7. Permissions-Policy เป็นการควบคุมการเปิดใช้งานเช่น กล้อง หรือ ไมโครโฟน หรือ ฟีเจอร์อื่น ๆ
            ตัวอย่างการกำหนดค่า เช่น geolocation=(), camera=(), microphone=()
        • การปกปิดเวอร์ชันไม่แสดงในส่วนของ Header สามารถกำหนดเพิ่มเติมดังนี้
          1. เมนู IIS Manager –> Configuration Editor
            • Section: system.webServer/security/requestFiltering กำหนด removeServerHeader เป็น True เพื่อไม่ให้แสดง เวอร์ชันของ server
            • Section: system.web/httpRuntime กำหนด enableVersionHeader เป็น False เพื่อไม่ให้แสดงเวอร์ชันของ IIS หรือ ASP.Net
          2. กำหนด expose_php = Off ใน php.ini เพื่อไม่ให้แสดงเวอร์ชันของ php
          3. ลบ X-Powered-By ออกจาก HTTP Response Headers
      4. กำหนด IP Address ส่วนของ Remote Address ใน Windows Defender Firewall with Advance Security – Inbound Rules เพื่อควบคุมการเข้าถึงจากคอมพิวเตอร์ที่ได้รับอนุญาตเท่านั้น
      5. ปิด port ที่ไม่ได้ใช้งาน
    • การกำหนดส่วนของเว็บ
      1. การเรียกใช้งานไรบรารีจากภายนอกเว็บไซต์ เช่น เดิม จะมีการเรียกใช้โดยอ้างอิงแบบ
        src=”https://code.jquery.com/jquery-3.7.1.min.js” ซึ่งจะไม่มีการตรวจสอบความน่าเชื่อถือ
        เพื่อสร้างความเชื่อมั่นว่าไรบรารีที่ใช้งานจะไม่มีการเปลี่ยนแปลง จึงมีการเพิ่มส่วนการตรวจสอบ integrity และ crossorigin ซึ่งสามารถเลือกใช้งาน Code Integration ได้จากเว็บ https://releases.jquery.com/jquery/ หรือ https://cdnjs.com/libraries ดังตัวอย่างนี้
        • src=”https://code.jquery.com/jquery-3.7.1.min.js” integrity=”sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=” crossorigin=”anonymous” หรือ
        • src=”https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js” integrity=”sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==” crossorigin=”anonymous” referrerpolicy=”no-referrer”
      2. การป้องกันการเรียกดู user data ผ่าน REST API ใน WordPress กรณีนี้ควรติดตั้งส่วนเสริมไม่ให้สามารถเรียกใช้งานผ่าน REST API โดยไม่มีการยืนยันตัวตนก่อน เช่น Disable WP REST API
    • หลังจาก กำหนดค่าเรียบร้อยแล้ว สามารถทดสอบได้ที่ https://www.ssllabs.com/ssltest/analyze.html คลิกเลือก Do not show the results on the boards ก่อนสแกน ด้วยครับ
    • Windows Server 2019 รองรับ TLS 1.2
    • Windows Server 2022 รองรับ TLS 1.2 และ TLS 1.3
      หมายเหตุ ทั้งนี้ Windows Server 2022 เพิ่มการรองรับ TLS 1.3 อย่างไรก็ตาม หากเปิดใช้งานทั้ง TLS 1.2 และ 1.3 Site Scanner จะส่งผลให้ได้เกรด A เท่านั้น เนื่องจากปัจจุบัน Windows Server ไม่รองรับการป้องกันการโจมตีแบบดาวน์เกรด หากไคลเอนต์ร้องขอ TLS 1.3 Windows จะยังคงอนุญาตให้ปรับไปใช้ TLS 1.2 ได้ และนั่นคือสาเหตุที่ Site Scanner รายงานเกรด A แทนที่จะเป็น A+
  • Install from scratch – licensing (2)

    ใช้ plugin ชื่อว่า WPO365 วิธีการตั้งค่าเพื่อใช้งาน single sign on ของ Microsoft365 ในการล็อคอินเข้าระบบของ WordPress

    Authentication
    1. ต้องเซ็ต HTTPS ที่เว็บให้เสร็จเรียบร้อย
    2. สำคัญ!!! จำเป็นต้องมีสิทธิ์ Global Administrator บน Azure
    3. Login เข้าเว็บ https://portal.azure.com มองหา Azure Active Directory
    4. ซ้ายมือเลื่อนหาคำว่า App registrations
    5. คลิก + New registration แล้วกรอกชื่อที่ต้องการในช่อง Name ในส่วนของ Supported account types เลือก Accounts in this organizational directory only (Prince of Songkla University only – Single tenant)
    1. เลื่อนลงมาจนเจอคำว่า Redirect URI (Optional) เลือก Web ในช่อง select platform และกรอกชื่อเว็บ https://licensing.psu.ac.th/wp-admin/ ลงไปในช่องถัดมา แล้วคลิก Register
    1. หน้าถัดไป คลิก Token configuration บนเมนูซ้ายมือ จากนั้นคลิก + Add optional claim เลือก Token Type เป็น ID และเลือกหัวข้อด้านล่าง ดังนี้ email, family_name, given_name และ upn แล้วคลิกปุ่ม Add จะมีให้ Add optional claim เพิ่ม ให้ติ๊กถูกแล้วกด Turn on the Microsoft Graph email, profile permission (required for claims to appear in token). แล้ว Add จะได้
    1. คลิก API permissions ที่เมนูซ้ายมือ แล้วคลิก + Add permission คลิก Microsoft Graph แล้วคลิก Delegated และเลือกหัวข้อดังต่อไปนี้ (พิมพ์ในช่อง Search ได้เลย) email, offline_access, openid และ profile แล้วคลิก Add permissions

    จากนั้นคลิก Grant admin consent for Prince of Songkla University แล้วคลิก Yes

    1. คลิก Certificates & Secrets ที่เมนูซ้ายมือ คลิก  + New client secret ตั้งชื่อในช่อง Description และเลือกวันหมดอายุ คลิก Add จากนั้นให้ copy ค่าของช่อง Value เก็บไว้
    1. กลับมาที่ https://licensing.psu.ac.th หลังจากติดตั้งปลักอินชื่อ WPO365 | LOGIN และเปิดใช้งาน (activate) เรียบร้อยแล้ว
    2. คลิก WPO365 ที่ด้านซ้ายมือล่างสุด เปิดใช้งาน SINGLE SIGN-ON (SSO) แล้วคลิก configure
    3. กรอกข้อมูลดังนี้
      1. Select Identity Provider (IdP) เลือก Azure AD (default)
      2. Select SSO-protocol เลือก OpenID Connect (default)
      3. Select OpenID Connect flow เลือก Auth.-code flow (recommended)
      4. Application (Client) Secret นำค่าที่ copy เก็บไว้ในข้อ 9 มาใส่ช่องนี้
      5. Directory (tenant) ID นำค่าที่ได้จากหน้า Overview ของ lsc ชื่อฟิลด์ Directory (tenant) ID
      6. Application (Client) ID นำค่าที่ได้จากหน้า Overview ของ lsc ชื่อฟิลด์ Application (client) ID
      7. Redirect URI View in Azure Portal ใส่ว่า https://licensing.psu.ac.th/
      8. Domain hint View Custom domain names ใส่ว่า psu.ac.th, email.psu.ac.th
      9. Add prompt=select_account parameter ติ๊กถูก
      10. กด Save configuration ด้านล่าง แล้วจะมีหน้า Important ขึ้นมากด Confirm
    4. คลิกแท็บ User Registeration ด้านล่างกรอกดังนี้
      1. Custom domains View Custom domain names กรอก psu.ac.th และคลิก + เพิ่มอีกช่องพิมพ์ email.psu.ac.th
      2. เลื่อนลงมาล่างสุดกด Save configuration
    5. คลิกแท็บ Plugin self-test เลื่อนลงมาด้านล่าง กด Start self-test จะถูก redirect ไปหน้า log in ของ mail ให้ Log in ตามปกติ แล้วจะเด้งกลับมาหน้าของ https://licensing.psu.ac.th หากล็อคอินไม่ผ่านให้กลับไปหน้า https://portal.azure.com คลิก App registrations แล้วเลือก lsc แล้วคลิก Authentication คลิก Add URI เพิ่ม https://licensing.psu.ac.th เข้าไปอีก 1 url กด save กลับไปล็อคอินอีกครั้งน่าจะได้แล้ว
  • ติดตั้ง CMSimple version 5.3 ใน VM ของ miniONE KVM

    เป็น CMS Web Server อย่างง่าย ไม่มี database ติดตั้งลงใน OS ที่เป็น Ubuntu Server 20.04 ที่ได้ติดตั้ง apache2 web server (เปิด port 80) พร้อม php ไว้แล้วด้วยคำสั่ง sudo apt install apache2 php php-xml

    ขั้นตอน

    1. ไปที่เครื่อง Ubuntu Desktop ของเรา เข้าเว็บเบราว์เซอร์ OpenNebula Sunstone และเปิด VM ที่เป็น Ubuntu Server (สมมติว่า VM มี IP 172.16.100.3)
    2. เปิดเพจของ CMSimple
    3. ดาวน์โหลด CMSimple_5-3.zip
    4. ใช้คำสั่ง scp คัดลอกไฟล์ไปไว้ใน VM
      scp CMSimple_5-3.zip  papa@172.16.100.3
      เสร็จแล้ว ssh เข้าไปใน VM
      ssh  papa@172.16.100.3
    5. แตกไฟล์ CMSimple_5-3.zip
    6. หากยังไม่ได้ติดตั้ง apache2 และ php ให้ใช้คำสั่ง sudo apt install apache2 php และคำสั่ง sudo apt  install  php-xml
    7. นำไปไว้ใน /var/www/html/cms/
      sudo  mv  CMSimple_5-3  /var/www/html/cms
      sudo  chown  -R  www-data:www-data  /var/www/html/cms
    8. ทำ restart apache2
      sudo su –
      service apache2 restart
    9. ไปที่หน้าเว็บ http://VM_IP/cms/
    10. ศึกษาวิธีตั้ง Password ครั้งแรกได้จากไฟล์ readme.php
    11. ทำขั้นตอน setup Password ของ CMSimple
      sudo su –
      cd /var/www/html/cms/
      cp  ./setup/setupControl.php  .
      chmod 666 setupControl.php
    12. ไปที่หน้า login ของเว็บ http://VM_IP/cms/ และใส่ Password 
    13. ไปเปลี่ยน Password ที่เมนู Settings > CMS
    14. ตั้ง Password และคลิก Save
    15. ลอง logout แล้ว login ใหม่
    16. ใส่ Password ที่ตั้งใหม่
    17. ตอนนี้ก็จัดการ Pages ต่าง ๆ ที่ให้มาเป็นตัวอย่าง ศึกษาวิธีทำเพจได้เอง 
    18. เมื่อ logout ทุกครั้ง จะมีแจ้งว่า ได้บันทึกเพจไว้ 2 ไฟล์ พร้อมวันที่บันทึกเป็นสำเนา

    จบ คิดว่า มันง่ายดีในการนำมาใช้ทดสอบ และ ใช้งานได้จริงสำหรับเว็บเพจง่าย ๆ

  • การเชื่อมต่อ OAuth2 ด้วย WordPress

    อยาก  Login ด้วย OAuth2 กับ WordPress ต้องทำอย่างไร

    สำหรับตัวอย่างนี้จะทำการติดตั้งบน WordPress 5.1 ผ่าน Plugin Simple Single Sign On

    • หลังจากติดตั้ง WordPress เสร็จ เข้าหน้า Administrator แล้วทำการกด Install เพื่อเข้าไปยังหน้าติดตั้ง Extension เพิ่มเติม ทำการเพิ่มปลั๊กอินใหม่ดังรูป

    • ทำการค้นหา single sign on และทำการ Install Now

    • จากนั้นทำการกด Activate

    • จากนั้นทำการตั้งค่าโดยข้าม Step 1 ไปตั้งค่า Step 2 เนื่องจากได้มีการตั้งค่า WP OAuth Server ให้รองรับไว้อยู่แล้ว

    • หลังจากนั้นทำการทดสอบ Login โดยกดปุ่ม Single Sign On

    เพิ่งเท่านี้ก็จะสามารถใช้งานได้ แต่ยังเป็นแบบเลือกได้ว่าจะ Login แบบ Local หรือผ่าน OAuth โดยอาจจะทำเป็นปุ่มในหน้าแรกเพื่อให้กด Login ก็ได้เช่นกัน

  • การเชื่อมต่อ OAuth2 ด้วย Joomla

    อยาก  Login ด้วย OAuth2 กับ Joomla ต้องทำอย่างไร

                 สำหรับตัวอย่างนี้จะทำการติดตั้งบน Joomla 3.9.3 ผ่าน Plugin MiniOrange OAuth Client

    • หลังจากติดตั้ง Joomla เสร็จ เข้าหน้า Administrator แล้วทำการกด Install เพื่อเข้าไปยังหน้าติดตั้ง Extension เพิ่มเติม

    • หลังจากนั้นกด Add Install from Web

    • ค้นหาชื่อ oauth

    • ติดตั้ง miniOrange OAuth Client 

    • หลังจากติดตั้งเสร็จให้ทำการเปิด Plugins เพิ่มเติมดังรูป

    • จากนั้นทำการตั้งค่าโดยสำหรับ miniOrange ต้องสมัครใช้งานก่อน เพราะมีทั้งแบบฟรีและไม่ฟรี แต่เราจะใช้เฉพาะในส่วนของฟรี

    • ในการสมัครต้องใส่ email และตั้งรหัสผ่าน

    • หลังจากติดตั้ง Joomla เสร็จให้ทำการกด Install เพื่อเข้าไปยังหน้าติดตั้ง Extension เพิ่มเติม

    • จากนั้นทำการตั้งค่าเกี่ยวกับ OAuth

    • สามารถกดทดสอบการ Authen ได้ที่ปุ่ม Test Configuration

    • จะปรากฎหน้า Login เพื่อเข้าสู่ระบบ

     

    •  

    • หลังจาก Login เสร็จจะคืนค่า User Profile ดังรูป

              ในการเอาไปใช้งานต่อให้ทำการสร้างปุ่ม ชี้ไปยัง http://localhost/joomla/?morequest=oauthredirect&app_name=other เพื่อเข้าใช้งาน OAuth ลองไปทำต่อดูครับ

  • WordPress new editor

    อีกไม่นาน WordPress เวอร์ชั่นถัดไป จะใช้ Editor ชื่อ Gutenberg เป็น default editor แทน โดยขยับให้ editor แบบเดิมไปเป็น plugin ชื่อ Classic editor ครับ

    วันนี้ก็ลองใช้ editor ใหม่นี้ และโพสต์เพจนี้ดูว่าใช้ยากมั้ย

    เมื่อ upgrade WordPress เป็น 4.9.8 ก็จะพบเพจเชิญชวน และ คำแนะนำ

    หน้าตา editor ก็ ประมาณนี้ 

    ตัวเลือกก็เป็น ปุ่มเปิด/ปิด ด้วยเครื่องหมาย สามเหลี่ยม เพื่อเปิดรายการมาให้เลือก เช่น categories ที่ต้องการ เมื่ออยู่ที่แท็บ document แต่ถ้า cursor วางอยู่ในข้อความ จะย้ายไปที่แท็บ Block จะเห็น ตัวเลือกอีกชุด

    น่าจะใช้งานได้ดีกว่าแบบเดิม