feat: Block common exploits in Security

This commit is contained in:
Robert 2022-10-02 12:32:55 +03:00
parent e95e615c18
commit 5c1e422d7d
2 changed files with 334 additions and 43 deletions

View File

@ -32,37 +32,235 @@ export default (domains, global) => {
config.push(['# security headers', '']);
config.push(['add_header X-XSS-Protection', '"1; mode=block" always']);
config.push(['add_header X-Content-Type-Options', '"nosniff" always']);
config.push(['add_header Referrer-Policy', `"${global.security.referrerPolicy.computed}" always`]);
config.push([
'add_header Referrer-Policy',
`"${global.security.referrerPolicy.computed}" always`,
]);
if (global.security.contentSecurityPolicy.computed)
config.push(['add_header Content-Security-Policy', `"${global.security.contentSecurityPolicy.computed}" always`]);
config.push([
'add_header Content-Security-Policy',
`"${global.security.contentSecurityPolicy.computed}" always`,
]);
if (global.security.permissionsPolicy.computed)
config.push(['add_header Permissions-Policy', `"${global.security.permissionsPolicy.computed}" always`]);
config.push([
'add_header Permissions-Policy',
`"${global.security.permissionsPolicy.computed}" always`,
]);
// Every domain has HSTS enabled, and they all have same hstsSubdomains/hstsPreload settings
if (commonHsts(domains)) {
const commonHSTSSubdomains = domains.length && domains[0].https.hstsSubdomains.computed;
const commonHSTSPreload = domains.length && domains[0].https.hstsPreload.computed;
config.push(['add_header Strict-Transport-Security', `"max-age=31536000${commonHSTSSubdomains ? '; includeSubDomains' : ''}${commonHSTSPreload ? '; preload' : ''}" always`]);
const commonHSTSSubdomains =
domains.length && domains[0].https.hstsSubdomains.computed;
const commonHSTSPreload =
domains.length && domains[0].https.hstsPreload.computed;
config.push([
'add_header Strict-Transport-Security',
`"max-age=31536000${
commonHSTSSubdomains ? '; includeSubDomains' : ''
}${commonHSTSPreload ? '; preload' : ''}" always`,
]);
}
config.push(['# . files', '']);
config.push(['location ~ /\\.(?!well-known)', {
deny: 'all',
}]);
config.push([
'location ~ /\\.(?!well-known)',
{
deny: 'all',
},
]);
// Security.txt
if (global.security.securityTxt.computed) {
config.push(['# security.txt', '']);
config.push(['location /security.txt', {
return: '301 /.well-known/security.txt',
}]);
config.push([
'location /security.txt',
{
return: '301 /.well-known/security.txt',
},
]);
// Custom security.txt path
config.push(['location = /.well-known/security.txt', {
alias: `${global.security.securityTxtPath.value}`,
}]);
config.push([
'location = /.well-known/security.txt',
{
alias: `${global.security.securityTxtPath.value}`,
},
]);
}
if (global.security.blockCommonExploits.computed) {
// Block SQL Injections
config.push(['## Block SQL injections', '']);
config.push(['set $block_sql_injections', '0']);
config.push([
'if ($query_string ~ "union.*select.*(")',
{
set: '$block_sql_injections 1',
},
]);
config.push([
'if ($query_string ~ "union.*all.*select.*")',
{
set: '$block_sql_injections 1',
},
]);
config.push([
'if ($query_string ~ "concat.*(")',
{
set: '$block_sql_injections 1',
},
]);
config.push(['if ($block_sql_injections = 1)', { return: '403' }]);
// Block file injections
config.push(['## Block file injections', '']);
config.push(['set $block_file_injections', '0']);
config.push([
'if ($query_string ~ "[a-zA-Z0-9_]=http://")',
{ set: '$block_file_injections 1' },
]);
config.push([
'if ($query_string ~ "[a-zA-Z0-9_]=(..//?)+")', // eslint-disable-line
{ set: '$block_file_injections 1' },
]);
config.push([
'if ($query_string ~ "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+")', // eslint-disable-line
{ set: '$block_file_injections 1' },
]);
config.push(['if ($block_file_injections = 1)', { return: '403' }]);
// Block common exploits
config.push(['## Block common exploits', '']);
config.push(['set $block_common_exploits', '0']);
config.push([
'if ($query_string ~ "(<|%3C).*script.*(>|%3E)")',
{
set: '$block_common_exploits 1',
},
]);
config.push([
'if ($query_string ~ "GLOBALS(=|[|%[0-9A-Z]{0,2})")',
{
set: '$block_common_exploits 1',
},
]);
config.push([
'if ($query_string ~ "_REQUEST(=|[|%[0-9A-Z]{0,2})")',
{
set: '$block_common_exploits 1',
},
]);
config.push([
'if ($query_string ~ "proc/self/environ")',
{
set: '$block_common_exploits 1',
},
]);
config.push([
'if ($query_string ~ "mosConfig_[a-zA-Z_]{1,21}(=|%3D)")',
{
set: '$block_common_exploits 1',
},
]);
config.push([
'if ($query_string ~ "base64_(en|de)code(.*)")',
{
set: '$block_common_exploits 1',
},
]);
config.push(['if ($block_common_exploits = 1)', { return: '403' }]);
// Block spam
config.push(['# Block spam', '']);
config.push(['set $block_spam', '0']);
config.push([
'if ($query_string ~ "\b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)\b")', // eslint-disable-line
{
set: '$block_spam 1',
},
]);
config.push([
'if ($query_string ~ "\b(erections|hoodia|huronriveracres|impotence|levitra|libido)\b")', // eslint-disable-line
{
set: '$block_spam 1',
},
]);
config.push([
'if ($query_string ~ "\b(ambien|bluespill|cialis|cocaine|ejaculation|erectile)\b")', // eslint-disable-line
{
set: '$block_spam 1',
},
]);
config.push([
'if ($query_string ~ "\b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)\b")', // eslint-disable-line
{
set: '$block_spam 1',
},
]);
config.push(['if ($block_spam = 1)', { return: '403' }]);
// Block user agents
config.push(['$Block user agents', '']);
config.push(['set $block_user_agents', '0']);
config.push(['# Disable Akeeba Remote Control 2.5 and earlier', '']);
config.push([
'if ($http_user_agent ~ "Indy Library")',
{
set: '$block_user_agents 1',
},
]);
config.push(['# Common bandwidth hoggers and hacking tools.', '']);
config.push([
'if ($http_user_agent ~ "libwww-perl")',
{
set: '$block_user_agents 1',
},
]);
config.push([
'if ($http_user_agent ~ "GetRight")',
{
set: '$block_user_agents 1',
},
]);
config.push([
'if ($http_user_agent ~ "GetWeb!")',
{
set: '$block_user_agents 1',
},
]);
config.push([
'if ($http_user_agent ~ "Go!Zilla")',
{
set: '$block_user_agents 1',
},
]);
config.push([
'if ($http_user_agent ~ "Download Demon")',
{
set: '$block_user_agents 1',
},
]);
config.push([
'if ($http_user_agent ~ "Go-Ahead-Got-It")',
{
set: '$block_user_agents 1',
},
]);
config.push([
'if ($http_user_agent ~ "TurnitinBot")',
{
set: '$block_user_agents 1',
},
]);
config.push([
'if ($http_user_agent ~ "GrabNet")',
{
set: '$block_user_agents 1',
},
]);
config.push(['if ($block_user_agents = 1)', { return: '403' }]);
}
// Done!

View File

@ -32,7 +32,13 @@ THE SOFTWARE.
</div>
<div class="field-body">
<div class="field">
<div :class="`control${referrerPolicyChanged ? ' is-changed' : ''}`">
<div
:class="
`control${
referrerPolicyChanged ? ' is-changed' : ''
}`
"
>
<VueSelect
v-model="referrerPolicy"
:options="$props.data.referrerPolicy.options"
@ -43,25 +49,48 @@ THE SOFTWARE.
</div>
</div>
<div :class="`field is-horizontal${hasWordPress && !hasUnsafeEval ? ' is-aligned-top' : ''}`">
<div
:class="
`field is-horizontal${
hasWordPress && !hasUnsafeEval ? ' is-aligned-top' : ''
}`
"
>
<div class="field-label">
<label class="label">Content-Security-Policy</label>
</div>
<div class="field-body">
<div class="field">
<div :class="`control${contentSecurityPolicyChanged ? ' is-changed' : ''}`">
<div
:class="
`control${
contentSecurityPolicyChanged
? ' is-changed'
: ''
}`
"
>
<input
v-model="contentSecurityPolicy"
class="input"
type="text"
:placeholder="$props.data.contentSecurityPolicy.default"
:placeholder="
$props.data.contentSecurityPolicy.default
"
/>
</div>
<div v-if="hasWordPress && !hasWordPressUnsafeEval" class="control">
<div
v-if="hasWordPress && !hasWordPressUnsafeEval"
class="control"
>
<label class="text message is-warning">
<span
class="message-body"
v-html="$t('templates.globalSections.security.whenUsingWordPressUnsafeEvalIsOftenRequiredToAllowFunctionality')"
v-html="
$t(
'templates.globalSections.security.whenUsingWordPressUnsafeEvalIsOftenRequiredToAllowFunctionality'
)
"
></span>
</label>
</div>
@ -75,7 +104,13 @@ THE SOFTWARE.
</div>
<div class="field-body">
<div class="field">
<div :class="`control${permissionsPolicyChanged ? ' is-changed' : ''}`">
<div
:class="
`control${
permissionsPolicyChanged ? ' is-changed' : ''
}`
"
>
<input
v-model="permissionsPolicy"
class="input"
@ -93,10 +128,17 @@ THE SOFTWARE.
</div>
<div class="field-body">
<div class="field">
<div :class="`control${serverTokensChanged ? ' is-changed' : ''}`">
<div
:class="
`control${serverTokensChanged ? ' is-changed' : ''}`
"
>
<div class="checkbox">
<PrettyCheck v-model="serverTokens" class="p-default p-curve p-fill p-icon">
{{ $t('common.enable') }}
<PrettyCheck
v-model="serverTokens"
class="p-default p-curve p-fill p-icon"
>
{{ $t("common.enable") }}
</PrettyCheck>
</div>
</div>
@ -110,10 +152,17 @@ THE SOFTWARE.
</div>
<div class="field-body">
<div class="field">
<div :class="`control${limitReqChanged ? ' is-changed' : ''}`">
<div
:class="
`control${limitReqChanged ? ' is-changed' : ''}`
"
>
<div class="checkbox">
<PrettyCheck v-model="limitReq" class="p-default p-curve p-fill p-icon">
{{ $t('common.enable') }}
<PrettyCheck
v-model="limitReq"
class="p-default p-curve p-fill p-icon"
>
{{ $t("common.enable") }}
</PrettyCheck>
</div>
</div>
@ -129,8 +178,11 @@ THE SOFTWARE.
<div class="field">
<div :class="`control${securityTxt ? ' is-changed' : ''}`">
<div class="checkbox">
<PrettyCheck v-model="securityTxt" class="p-default p-curve p-fill p-icon">
{{ $t('common.enable') }}
<PrettyCheck
v-model="securityTxt"
class="p-default p-curve p-fill p-icon"
>
{{ $t("common.enable") }}
</PrettyCheck>
</div>
</div>
@ -138,13 +190,20 @@ THE SOFTWARE.
</div>
</div>
<div v-if="$props.data.securityTxt.computed" class="field is-horizontal">
<div
v-if="$props.data.securityTxt.computed"
class="field is-horizontal"
>
<div class="field-label">
<label class="label">security.txt path</label>
</div>
<div class="field-body">
<div class="field">
<div :class="`control${securityTxtChanged ? ' is-changed' : ''}`">
<div
:class="
`control${securityTxtChanged ? ' is-changed' : ''}`
"
>
<input
v-model="securityTxtPath"
class="input"
@ -155,6 +214,30 @@ THE SOFTWARE.
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Block common exploits</label>
</div>
<div class="field-body">
<div class="field">
<div
:class="
`control${blockCommonExploits ? ' is-changed' : ''}`
"
>
<div class="checkbox">
<PrettyCheck
v-model="blockCommonExploits"
class="p-default p-curve p-fill p-icon"
>
{{ $t("common.enable") }}
</PrettyCheck>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
@ -180,7 +263,8 @@ THE SOFTWARE.
enabled: true,
},
contentSecurityPolicy: {
default: 'default-src \'self\' http: https: ws: wss: data: blob: \'unsafe-inline\'; frame-ancestors \'self\';',
default:
'default-src \'self\' http: https: ws: wss: data: blob: \'unsafe-inline\'; frame-ancestors \'self\';',
enabled: true,
},
permissionsPolicy: {
@ -203,28 +287,37 @@ THE SOFTWARE.
default: '~/security.txt',
enabled: true,
},
blockCommonExploits: {
default: false,
enabled: true,
},
};
export default {
name: 'GlobalSecurity', // Component name
display: 'templates.globalSections.security.security', // Display name for tab (i18n key)
key: 'security', // Key for data in parent
delegated: delegatedFromDefaults(defaults), // Data the parent will present here
name: 'GlobalSecurity', // Component name
display: 'templates.globalSections.security.security', // Display name for tab (i18n key)
key: 'security', // Key for data in parent
delegated: delegatedFromDefaults(defaults), // Data the parent will present here
components: {
PrettyCheck,
VueSelect,
},
props: {
data: Object, // Data delegated back to us from parent
data: Object, // Data delegated back to us from parent
},
computed: {
...computedFromDefaults(defaults, 'security'), // Getters & setters for the delegated data
...computedFromDefaults(defaults, 'security'), // Getters & setters for the delegated data
hasWordPress() {
return this.$parent.$parent.$data.domains.some(d => d && d.php.wordPressRules.computed);
return this.$parent.$parent.$data.domains.some(
d => d && d.php.wordPressRules.computed,
);
},
hasWordPressUnsafeEval() {
return this.$props.data.contentSecurityPolicy.computed
.match(/(default|script)-src[^;]+'self'[^;]+'unsafe-inline'[^;]+'unsafe-eval'[^;]*;/) !== null;
return (
this.$props.data.contentSecurityPolicy.computed.match(
/(default|script)-src[^;]+'self'[^;]+'unsafe-inline'[^;]+'unsafe-eval'[^;]*;/,
) !== null
);
},
hasWarnings() {
return this.hasWordPress && !this.hasWordPressUnsafeEval;