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. ขอให้สนุก..

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *