Tag: content security policy

  • 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('send_headers', 'simple_csp_headers');
    /*ยกเว้นหน้าที่ไม่ต้องการทำ csp */
    function simple_csp_headers() {
        $pages = array('cd-key');
        if (is_admin() || is_page($pages)) {
            if (!headers_sent()) {
                header("Content-Security-Policy: upgrade-insecure-requests");
            }
        }
    }
    
    if (function_exists('litespeed_autoload')) {
        add_filter('litespeed_buffer_after', 'process_csp_buffer', 0);
    } else {
        add_action('template_redirect', 'csp_ob_start');
    }
    
    function csp_ob_start() {
        if (!is_admin()) {
            ob_start('process_csp_buffer');
        }
    }
    
    function process_csp_buffer($content) {
        $nonce = base64_encode(random_bytes(16));
        $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;
        }
    
        $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;
    }
    
    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. จบ… ขอให้สนุก
  • Hardening your HTTP response headers

    Introduction

    HTTP Response headers คือ ค่าของสตริงที่ส่งกลับมาจากเซิร์ฟเวอร์ที่มีเนื้อหาตามที่ถูกร้องขอ โดยปกติจะใช้บอกข้อมูลทางเทคนิค เช่น เบราเซอร์ควรแคชเนื้อหา, ประเภทของเนื้อหาคืออะไร, ซอฟต์แวร์ที่ทำงานบนเซิร์ฟเวอร์ และอื่น ๆ   HTTP Response headers ถูกใช้เพื่อส่งต่อนโยบายความปลอดภัยไปยังเบราเซอร์  ทำให้การเปิดเว็บไซต์ของเรามีความปลอดภัยเพิ่มมากขึ้น

    Header ของ Apache2 ที่ควรต้องใส่เพื่อเพิ่มความปลอดภัยมีดังนี้

    Content Security Policy

    Header เรื่อง Content Security Policy (CSP) ช่วยให้กำหนดต้นทางของเนื้อหาที่อนุญาตสำหรับเว็บไซต์ โดยการจำกัดเนื้อหาที่เบราเซอร์สามารถโหลดได้ ได้แก่ js และ css

    สามารถสร้าง CSP ได้จาก https://report-uri.com/home/generate ทั้งนี้ต้องทดสอบการทำงานทุกครั้งเนื่องจาก การกำหนดค่าบางอย่างอาจทำให้เว็บไซต์ ทำงานไม่ถูกต้อง ดูรายละเอียดเพิ่มเติมได้ที่ https://scotthelme.co.uk/content-security-policy-an-introduction/ สำหรับ Apache2 เพิ่ม Header ต่อไปนี้ ในแฟ้มของไซต์ที่ต้องการ เช่น /etc/apache2/site-enabled/lsc-ssl.conf  หรือแฟ้ม .htaccess ในไซต์ที่ต้องการ (ซึ่งแนะนำว่าใช้ .htaccess จะได้ไม่ต้องรีสตาร์ทเซิร์ฟเวอร์) เพื่อเปิดการใช้งาน CSP


    Header always set Content-Security-Policy "default-src https: data: 'unsafe-inline' 'unsafe-eval'"

    HTTP Strict Transport Security (HSTS)

    เว็บไซต์ ต้องมีการตั้งค่าให้ redirect จาก HTTP ไปยัง HTTPS เสมอ และ HSTS จะเป็น Header ที่กำหนดให้เบราเซอร์จำสถานะของ HTTPS เอาไว้แม้ว่าจะเป็นการเปิดจาก bookmark ที่เป็น HTTP ก็ตาม ก็จะถูกบังคับให้เป็น HTTPS รายละเอียดเพิ่มเติม https://scotthelme.co.uk/hsts-the-missing-link-in-tls/ เช่น เมื่อเปิด http://licensing.psu.ac.th ก็จะถูก redirect ไป https://licensing.psu.ac.th

    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

    X-Frame-Options

    X-Frame-Options หรือ XFO header จะช่วยป้องกันผู้ใช้จากการโจมตีแบบ clickjacking ที่ผู้บุกรุกสามารถโหลด iframe จากไซต์ของเขาบนไซต์ของเราได้ ซึ่งทำให้ผู้ใช้งานเว็บไซต์ของเราเชื่อว่าไม่อันตราย!! รายละเอียดเพิ่มเติม https://www.troyhunt.com/clickjack-attack-hidden-threat-right-in/

    Header always set X-Frame-Options "SAMEORIGIN"

    X-Xss-Protection

    Header นี้ใช้กำหนดค่าการป้องกัน XSS ที่มีอยู่บนเบราเซอร์ต่างๆ โดยการตั้งค่าจะมี 0 คือ ปิดการทำงาน 1 คือเปิดการทำงาน และ 1; mode=block ซึ่งจะกำหนดให้เบราเซอร์ทำการบล็อคการกระทำใดๆ ก็ตามที่มากกว่าการล้างข้อมูลสคริปต์

    Header always set X-Xss-Protection "1; mode=block"

    X-Content-Type-Options

    X-Content-Type-Options ใช้ในการป้องกันการโจมตีผ่านทางช่องโหว่ MIME sniffing ซึ่งจะเกิดเมื่อ เว็บไซต์อนุญาตให้ผู้ใช้อัพโหลดเนื้อหาไปยังเซิร์ฟเวอร์ ซึ่งผู้ใช้อาจเปลี่ยนหรือซ่อนไฟล์อันตราย แล้วอัพโหลดขึ้นเซิร์ฟเวอร์ รายละเอียดเพิ่มเติม https://www.keycdn.com/support/x-content-type-options/

    Header always set X-Content-Type-Options "nosniff"

    เบื้องต้นแนะนำเท่านี้ก่อนครับ พิเศษ!! สำหรับผู้ใช้งาน wordpress มีปลั๊กอินชื่อ HTTP Headers ใช่ตั้งค่า Header ต่างๆ ที่เล่ามาข้างต้นได้อย่างสบายใจหายห่วง!!! ไม่ต้องแก้ .htaccess ไม่ต้องแก้ config ใด ๆ  เมื่อติดตั้งเสร็จแล้วจะพบว่ามี Header อีกมากที่สามารถตั้งค่าเพิ่มเติมได้ ซึ่งจะกล่าวอีกในครั้งต่อ ๆ ไป

    ต้นฉบับ

    https://scotthelme.co.uk/hardening-your-http-response-headers/

    มีวิธีการเซ็ตสำหรับ Nginx และ IIS สามารถดูเพิ่มเติมได้ครับ
    อย่าลืม!!

    ตรวจ HTTP Response ได้ที่ https://securityheaders.com

    จบขอให้สนุก