使い方
- インストールコマンドをコピーし、自分の環境へ導入する前にソースを確認します。
- 必要なツール、認証情報、対応環境を原文で確認します。
- 入力と成果物を指定し、出力を資料と照合します。
npx skills add https://github.com/wpultimatesecurity/wordpress-security-skills --skill filesystem-security開始前の確認
- 導入にはモデル契約、API枠、ファイルへのアクセス許可は含まれません。
- スター数や導入数は活動の指標であり、独立した品質・安全性試験ではありません。
見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。
Filesystem security
When to use this skill
Use this skill whenever code touches the filesystem with paths derived from input:
- Building a file path from
$_GET/$_POST/$_REQUEST. include,require,readfile,file_get_contents, orunlinkof a dynamic file.- Serving downloads, exports, or logs based on a request parameter.
- Deleting files selected by the user.
- Using
WP_Filesystemfor writes, deletes, or directory creation.
Path traversal and local file inclusion are high-impact WordPress vulnerabilities — ../../wp-config.php or a malicious uploaded .php can expose secrets or achieve RCE.
Related: see the file-upload-security skill for $_FILES handling and the object-injection-deserialization skill for uploaded data formats.
Core principles (and why they matter)
- Never `include` / `require` a user-controlled path. That is remote/local code
execution. Keep templates and includes hardcoded.
- Constrain every file operation to a known base directory. Resolve with
realpath()
and assert the result starts with the allowed base path.
- `validate_file()` catches traversal, drive letters, and `:` characters. A non-zero
return value means the path is suspect.
- Use `sanitize_file_name()` and `wp_normalize_path()`. Normalize separators and strip
dangerous characters before building paths.
- Use WordPress deletion helpers.
wp_delete_file()andwp_delete_file_from_directory()
are safer than raw unlink().
- Use `WP_Filesystem` for writes. It handles permissions, credentials, and stream
wrappers consistently across hosts.
Step-by-step implementation
- Reject any request that wants to
include/requirea file based on input; use a hardcoded
allowlist of template files instead.
- For read/delete handlers:
- Verify nonce + capability.
- Sanitize the filename with
sanitize_file_name(). - Optionally run
validate_file(). - Build the target path under a known base directory.
- Resolve with
realpath()and confirm it stays under the base.
- For writes/deletes, prefer
WP_Filesystemwithrequest_filesystem_credentials(). - Never expose the full server path in errors or URLs.
Supporting references
| Reference | Load when |
|---|---|
| Filesystem security checklist | Before final verification of the filesystem security controls. |
| Secure filesystem operations | Implementing confined file reads and deletion, and writes through WP_Filesystem. |
Common AI mistakes / anti-patterns
Mistake 1 — include $_GET['page']
// ❌ Insecure: local/remote code execution.
include 'templates/' . $_GET['page'] . '.php';// ✅ Secure: hardcoded allowlist of template files.
$allowed = array( 'dashboard', 'settings', 'logs' );
$page = isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : 'dashboard';
if ( ! in_array( $page, $allowed, true ) ) {
wp_die( esc_html__( 'Invalid page.', 'my-plugin' ), 400 );
}
include __DIR__ . '/templates/' . $page . '.php';Mistake 2 — unlink() of a user-named file
// ❌ Insecure: arbitrary file deletion via traversal.
unlink( WP_CONTENT_DIR . '/exports/' . $_POST['file'] );// ✅ Secure: sanitize, resolve, and confine to the base directory.
$base = realpath( WP_CONTENT_DIR . '/exports' );
$request = sanitize_file_name( wp_unslash( $_POST['file'] ?? '' ) );
$target = realpath( $base . '/' . $request );
if ( false === $target || strpos( $target, $base . DIRECTORY_SEPARATOR ) !== 0 ) {
wp_die( esc_html__( 'Invalid file.', 'my-plugin' ), 400 );
}
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Forbidden.', 'my-plugin' ), 403 );
}
wp_delete_file( $target );Mistake 3 — readfile() without base-directory containment
// ❌ Insecure: ../../wp-config.php is reachable.
readfile( '/var/www/uploads/' . $_GET['file'] );// ✅ Secure: resolve and confine.
$base = realpath( wp_upload_dir()['basedir'] );
$request = sanitize_file_name( wp_unslash( $_GET['file'] ?? '' ) );
$target = realpath( $base . '/' . $request );
if ( false === $target || strpos( $target, $base . DIRECTORY_SEPARATOR ) !== 0 ) {
wp_die( esc_html__( 'Invalid file.', 'my-plugin' ), 400 );
}
readfile( $target );Mistake 4 — Trusting basename() alone
// ❌ Insecure: basename does not prevent traversal to siblings.
$file = basename( $_GET['file'] );
readfile( '/var/www/uploads/' . $file );// ✅ Secure: basename plus realpath containment.
$base = realpath( '/var/www/uploads' );
$file = basename( sanitize_file_name( wp_unslash( $_GET['file'] ?? '' ) ) );
$target = realpath( $base . '/' . $file );
if ( false === $target || strpos( $target, $base . DIRECTORY_SEPARATOR ) !== 0 ) {
wp_die( esc_html__( 'Invalid file.', 'my-plugin' ), 400 );
}
readfile( $target );Mistake 5 — Writing files with raw PHP functions
// ❌ Insecure: no permission/credential handling, may fail on restrictive hosts.
file_put_contents( $path, $data );// ✅ Secure: use WP_Filesystem after requesting credentials.
function my_plugin_write_file( $path, $data ) {
global $wp_filesystem;
if ( ! function_exists( 'request_filesystem_credentials' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
$url = admin_url( 'admin-post.php?action=my_plugin_save' );
$creds = request_filesystem_credentials( $url );
if ( false === $creds || ! WP_Filesystem( $creds ) ) {
return new WP_Error( 'filesystem', __( 'Could not initialize filesystem.', 'my-plugin' ) );
}
return $wp_filesystem->put_contents( $path, $data, FS_CHMOD_FILE );
}Correct code examples
A complete safe file download/delete helper using base-directory containment is in `references/secure-filesystem-operations.php`.
Checklist
- [ ] No
include/requireuses user input; templates are selected from a hardcoded allowlist. - [ ] File paths are built under a known base directory.
- [ ]
realpath()resolves the final path and containment is verified. - [ ] Filenames pass through
sanitize_file_name()andwp_normalize_path(). - [ ]
validate_file()returns0for paths that include user input. - [ ] File delete operations use
wp_delete_file()orwp_delete_file_from_directory(). - [ ] File writes use
WP_Filesystemwithrequest_filesystem_credentials(). - [ ] State-changing handlers verify nonce + capability first.
- [ ] Full server paths are not exposed in errors or URLs.




