整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

如何在 Web 服务器文档根目录上设置只读文件权限

:如何对我存放在 /var/www/html/ 目录中的所有文件设置只读权限?

你可以使用 chmod 命令对 Linux/Unix/macOS/OS X/*BSD 操作系统上的所有文件来设置只读权限。这篇文章介绍如何在 Linux/Unix 的 web 服务器(如 Nginx、 Lighttpd、 Apache 等)上来设置只读文件权限。

如何设置文件为只读模式

语法为:

  1. ### 仅针对文件 ###

  2. chmod 0444 /var/www/html/*

  3. chmod 0444 /var/www/html/*.php

如何设置目录为只读模式

语法为:

  1. ### 仅针对目录 ###

  2. chmod 0444 /var/www/html/

  3. chmod 0444 /path/to/your/dir/

  4. # ***************************************************************************

  5. # 假如 web 服务器的用户/用户组是 www-data,文件拥有者是 ftp-data 用户/用户组

  6. # ***************************************************************************

  7. # 设置目录所有文件为只读

  8. chmod -R 0444 /var/www/html/

  9. # 设置文件/目录拥有者为 ftp-data

  10. chown -R ftp-data:ftp-data /var/www/html/

  11. # 所有目录和子目录的权限为 0445 (这样 web 服务器的用户或用户组就可以读取我们的文件)

  12. find /var/www/html/ -type d -print0 | xargs -0 -I {} chmod 0445 "{}"

找到所有 /var/www/html 下的所有文件(包括子目录),键入:

  1. ### 仅对文件有效 ###

  2. find /var/www/html -type f -iname "*" -print0 | xargs -I {} -0 chmod 0444 {}

然而,你需要在 /var/www/html 目录及其子目录上设置只读和执行权限,如此才能让 web 服务器能够访问根目录,键入:

  1. ### 仅对目录有效 ###

  2. find /var/www/html -type d -iname "*" -print0 | xargs -I {} -0 chmod 0544 {}

警惕写权限

请注意在 /var/www/html/ 目录上的写权限会允许任何人删除文件或添加新文件。也就是说,你可能需要设置一个只读权限给 /var/www/html/ 目录本身。

  1. ### web根目录只读 ###

  2. chmod 0555 /var/www/html

在某些情况下,根据你的设置要求,你可以改变文件的属主和属组来设置严格的权限。

  1. ### 如果 /var/www/html 目录的拥有人是普通用户,你可以设置拥有人为:root:root 或 httpd:httpd (推荐) ###

  2. chown -R root:root /var/www/html/

  3. ### 确保 apache 拥有 /var/www/html/ ###

  4. chown -R apache:apache /var/www/html/

关于 NFS 导出目录

你可以在 /etc/exports 文件中指定哪个目录应该拥有只读或者读写权限 [1]。这个文件定义各种各样的共享在 NFS 服务器和他们的权限。如:

  1. # 对任何人只读权限

  2. /var/www/html *(ro,sync)

  3. # 192.168.1.10(upload.example.com)客户端读写权限访问

  4. /var/www/html 192.168.1.10(rw,sync)

关于用于 MS-Windows客户端的 Samba(CIFS)只读共享

要以只读共享 sales,更新 smb.conf,如下:

  1. [sales]

  2. comment = Sales Data

  3. path = /export/cifs/sales

  4. read only = Yes

  5. guest ok = Yes

关于文件系统表(fstab)

你可以在 Unix/Linux 上的 /etc/fstab 文件中配置挂载某些文件为只读模式。

你需要有专用分区,不要设置其他系统分区为只读模式。

如下在 /etc/fstab 文件中设置 /srv/html 为只读模式。

  1. /dev/sda6 /srv/html ext4 ro 1 1

你可以使用 mount 命令重新挂载分区为只读模式[2](使用 root 用户)

  1. # mount -o remount,ro /dev/sda6 /srv/html

或者

  1. # mount -o remount,ro /srv/html

上面的命令会尝试重新挂载已挂载的文件系统到 /srv/html上。这是改变文件系统挂载标志的常用方法,特别是让只读文件改为可写的。这种方式不会改变设备或者挂载点。让文件变得再次可写,键入:

  1. # mount -o remount,rw /dev/sda6 /srv/html

  1. # mount -o remount,rw /srv/html

Linux:chattr 命令

你可以在 Linux 文件系统上使用 chattr 命令改变文件属性为只读[3],如:

  1. chattr +i /path/to/file.php

TML 中使用 <input> 元素表示单行输入框和 <textarea> 元素表示多行文本框。

HTML中使用的 <input> 元素在 JavaScript 中对应的是 HTMLInputElement 类型。HTMLInputElement 继承自 HTMLElement 接口:

interface HTMLInputElement extends HTMLElement {
    ...
}

HTMLInputElement 类型有一些独有的属性和方法:

  • name:表示 <input> 元素的名称。
  • disabled:表示 <input> 元素是否禁止使用。在表单提交时不会包含被禁止的 <input> 元素。
  • autofocus:表示页面加载时,该元素是否会自动获得焦点。
  • required:表示表单提交时,该 <input>元素是否为必填。
  • value:表示该 <input> 元素的值。
  • validity:返回一个 ValidityState 对象,表示 <input> 元素的校验状态。是只读属性。
  • validationMessage:表示 <input> 元素校验失败时,用户看到的报错信息。如果该元素不需要校验,或者通过校验,该属性为空字符串。是只读属性。
  • willValidate:表示表单提交时,<input> 元素是否会被校验。是只读属性。
  • select():选中 <input> 元素内部的所有文本。
  • click():模拟鼠标点击当前的 <input> 元素。

而在上述介绍 HTMLInputElement 类型中的属性时,type 属性要特别关注一下,因为根据 type 属性的改变,可以改变<input>的属性。

类型

描述

text

文本输入

password

密码输入

submit

表单数据提交

button

按钮

radio

单选框

checkbox

复选框

file

文件

hidden

隐藏的字段

image

定义图像作为提交按钮

reset

重置按钮

省略 type 属性与 type="text"效果一样, <input> 元素显示为文本框。

type 的值为text/password/number/时,会有以下属性对 <input> 元素有效。

属性

类型

描述

autocomplete

string

字符串on或off,表示<input>元素的输入内容可以被浏览器自动补全。

maxLength

long

指定<input>元素允许的最多字符数。

size

unsigned long

表示<input>元素的宽度,这个宽度是以字符数来计量的。

pattern

string

表示<input>元素的值应该满足的正则表达式

placeholder

string

表示<input>元素的占位符,作为对元素的提示。

readOnly

boolean

表示用户是否可以修改<input>的值。

min

string

表示<input>元素的最小数值或日期。

max

string

表示<input>元素的最大数值或日期。

selectionStart

unsigned long

表示选中文本的起始位置。如果没有选中文本,返回光标在<input>元素内部的位置。

selectionEnd

unsigned long

表示选中文本的结束位置。如果没有选中文本,返回光标在<input>元素内部的位置。

selectionDirection

string

表示选中文本的方向。可能的值包括forwardbackwardnone

下面创建一个 type="text" ,一次显示 25 个字符,但最多允许显示 50 个字符的文本框:

<input type="text" size="25" maxlength="50" value="initial value">

HTML 使用的 <textarea> 元素在 JavaScript 中对应的是 HTMLTextAreaElement 类型。HTMLTextAreaElement类型继承自 HTMLElement 接口:

interface HTMLTextAreaElement extends HTMLElement {
    ...
}

HTMLTextAreaElement 类型有一些独有的属性和方法:

  • form:只读属性,返回对父表单元素的引用。如果此元素未包含在表单元素中,则它可以是同一文档中 id 属性同 form 值的 <form> 元素,如果没有,该值为 null
  • type:只读属性,返回字符串textarea
  • valuestring类型,返回/设置控件中包含的原始值。
  • textLength:只读属性,long类型,返回<textarea>元素的value值的长度。与 value.length 相同。
  • defaultValuestring类型,返回/设置textarea元素的默认值。
  • placeholderstring类型,返回/设置 textarea 元素的占位符,作为对输入内容的提示。
  • rowsunsigned long类型,返回/设置textarea元素的可见文本行数。
  • colsunsigned long类型,返回/设置 textarea 元素的可见宽度。
  • autofocusboolean类型,返回/设置textarea元素在页面加载时自动获取焦点。
  • name:返回/设置 textarea 元素的名称。
  • disabledboolean类型,返回/设置 textarea 元素是否应当被禁用。
  • HTMLTextAreaElement.labelsNodeList类型,返回与此select元素关联的标签元素列表。
  • maxLengthlong类型,返回/设置元素可以输入的最大字符数。仅在值更改时才评估此约束。
  • minLengthlong类型,返回/设置元素可以输入的最小字符数。仅在值更改时才评估此约束。
  • accessKeyboolean类型,返回/设置访问 textarea 的键盘快捷键。
  • readOnlyboolean类型,返回/设置 textarea 元素是否为只读。
  • requiredboolean 类型,返回/设置 textarea 元素是否为必填项。
  • tabIndexlong类型,返回/设置 textarea 元素的 tab 键导航顺序中元素的位置。

下面创建一个高度为 25,宽度为 5 的 <textarea> 多行文本框。它与 <input> 不同的是,初始值显示在 <textarea>...</textarea> 之间:

<textarea rows="25" cols="5">initial value</textarea>

注意:处理文本框值的时候最好不要使用 DOM 方法,而应该使用 value 属性。

选择文本

<input><textarea> 都支持 select() 方法,该方法用于选中文本框中的所有内容。该方法的语法为:

select(): void

下面看一个示例:

let textbox = document.forms[0].elements["input-box"];
textbox.select();

也可以在文本框获得焦点时,选中文本框的内容:

textbox.addEventListener("focus", (event) => {
    event.target.select();
});

select 事件

当选中文本框中的文本或使用 select() 方法时,会触发 select 事件。

let textbox = document.forms[0].elements["textbox1"];
textbox.addEventListener("select", (event) => {
    console.log(`Text selected: ${textbox.value}`);
});

取得选中的文本

HTML5 对 select 事件进行了扩展,通过 selectionStartselectionEnd 属性获取文本选区的起点偏移量和终点偏移量。如下所示:

function getSelectedText(textbox){
    return textbox.value.substring(textbox.selectionStart,
textbox.selectionEnd);
}

注意:在 IE8 及更早版本不支持这两个属性。

选中部分文本

HTML5 提供了 setSelectionRange() 方法用于选中部分文本:

setSelectionRange(start, end, direction): void;
  • start:被选中的第一个字符的位置索引,从 0 开始。如果这个值比元素的 value 长度还大,则会被看做 value 最后一个位置的索引。
  • end:被选中的最后一个字符的下一个位置索引。如果这个值比元素的 value 长度还大,则会被看做 value 最后一个位置的索引。
  • direction:该参数是可选的字符串类型,表示选择方向,可以传入 “forward”/“backward”/“none” 三个值。

下面看一个例子:

<input type="text" id="text-sample" size="20" value="Hello World!">
<button onclick="selectText()">选中部分文本</button>
<script>
    function selectText() {
        let input = document.getElementById("text-sample");
        input.focus();
        input.setSelectionRange(4, 8);    // o Wo
    }
</script>

如果想要看到选中效果,必须让文本框获得焦点。

输入过滤

不同文本框经常需要保证输入特定类型或格式的数据,或许数据需要包含特定字符或必须匹配某个特定模式。而文本框并未提供验证功能,因此要配合 JavaScript 脚本实现输入过滤功能。

屏蔽字符

有些输入框需要出现或不出现特定字符。如果想要将输入框变成只读的,只需要使用 preventDefault()方法将按键都屏蔽:

input.addEventListener("keypress", (event) => {
    event.preventDefault();
});

而要屏蔽特定字符,就需要检查事件的 charCode 属性。如下所示,使用正则表达式实现只允许输入数字的输入框:

input.addEventListener("keypress", (event) => {
    if (!/\d/.test(event.key)) {
        event.preventDefault();
    }
});

还有一个问题需要处理:复制、粘贴及涉及Ctrl 键的其他功能。在除IE 外的所有浏览器中,前面代码会屏蔽快捷键Ctrl+C、Ctrl+V 及其他使用Ctrl 的组合键。因此,最后一项检测是确保没有按下Ctrl键,如下面的例子所示:

textbox.addEventListener("keypress", (event) => {
if (!/\d/.test(String.fromCharCode(event.charCode)) &&
event.charCode > 9 &&
!event.ctrlKey){
event.preventDefault();
}
});

最后这个改动可以确保所有默认的文本框行为不受影响。这个技术可以用来自定义是否允许在文本框中输入某些字符。

处理剪贴板

IE 是第一个实现了剪切板相关的事件以及通过JavaScript访问剪切板数据的浏览器,其它浏览器在后来也都支持了相同的事件和剪切板的访问,后来 HTML5 将其纳入了规范。以下是与剪切板相关的 6 个事件:

  • beforecopy:发生复制操作前触发。
  • copy:发生复制操作时触发。
  • beforecut:发生剪切操作前触发。
  • cut:发生剪切操作时触发。
  • beforepaste:发生粘贴操作前触发。
  • paste:发生粘贴操作时触发。

剪切板事件的行为及相关对象会因浏览器而异。在 Safari、Chrome 和 Firefox 中,beforecopybeforecutbeforepaste 事件只会在显示文本框的上下文菜单时触发,但 IE 不仅在这种情况下触发,也会在 copycutpaste 事件在所有浏览器中都会按预期触发。

在实际的事件发生之前,通过beforecopybeforecutbeforepaste 事件可以在向剪贴板发送或从中检索数据前修改数据。不过,取消这些事件并不会取消剪贴板操作。要阻止实际的剪贴板操作,必须取消 copycutpaste 事件。

剪贴板的数据通过 clipboardData 对象来获取,且clipboardData 对象提供 3 个操作数据的方法:

  • getData(format):从剪贴板获取指定格式的数据,接收的参数为指定获取的数据格式,如果没有指定数据格式或指定数据格式不存在,则此方法返回一个空字符串。
  • setData(format, data):给剪贴板赋予指定格式的数据,第一个参数指定数据格式,第二个参数为第一个参数赋予数据。如果指定数据格式不存在,则将其添加到数据存储的末尾;如果指定数据格式存在,则将数据替换在指定数据格式的位置。
  • clearData(format):删除指定格式的数据,接收的参数为指定要删除的数据格式,如果指定格式不存在,则此方法不执行任何操作;如果此参数为空字符串或未提供,则将删除所有格式的数据。

clipboardData 对象在 IE 中使用 window 获取,在 Firefox、Safari 和 Chrome 中使用 event 获取。为防止未经授权访问剪贴板,只能在剪贴板事件期间访问 clipboardData 对象;IE 会在任何时候都暴露 clipboardData 对象。因此,要兼容两者,最好在剪贴板事件期间使用该对象。

function getClipboardText(event){
    var clipboardData = (event.clipboardData || window.clipboardData);
    return clipboardData.getData("text");
}
function setClipboardText (event, value){
    if (event.clipboardData){
        return event.clipboardData.setData("text/plain", value);
    } else if (window.clipboardData){
        return window.clipboardData.setData("text", value);
    }
}

如果文本框只有数字,那剪贴时,就需要使用paste事件检查剪贴板上的文本是否无效。如果无效,可以取消默认行为:

input.addEventListener("paste", (event) => {
    let text = getClipboardText(event);
    if (!/^\d*$/.test(text)){
        event.preventDefault();
    }
});

注意:Firefox、Safari和Chrome只允许在onpaste事件中访问getData()方法。

自动切换

在 JavaScript 中,可以用在当前字段完成时自动切换到下一个字段的方式来增强表单字段的易用性。比如,常用手机号分为国家好加手机号。因此,我们设置 2 个文本框:

<form>
    <input type="text" name="phone1" id="phone-id-1" maxlength="4">
    <input type="text" name="phone2" id="phone-id-2" maxlength="11">
</form>

当文本框输入到最大允许字符数后,就把焦点移到下一个文本框,这样可以增加表单的易用性并加速数据输入。如下所示:

<script>
    function tabForward(event){
        let target = event.target;
        if (target.value.length == target.maxLength){
            let form = target.form;
            for (let i = 0, len = form.elements.length; i < len; i++) {
                if (form.elements[i] == target) {
                    if (form.elements[i+1]) {
                        form.elements[i+1].focus();
                    }
                    return;
                }
            }
        }
    }
    let inputIds = ["phone-id-1", "phone-id-2"];
    for (let id of inputIds) {
        let textbox = document.getElementById(id);
        textbox.addEventListener("keyup", tabForward);
    }
</script>

这里,tabForward() 函数通过比较用户输入文本的长度与 maxLength 属性的值来检测输入是否达到了最大长度。如果两者相等,就通过循环表中的元素集合找到当前文本框,并把焦点设置到下一个元素。

注意:上面的代码只适用于之前既定的标记,没有考虑可能存在的隐藏字段。

HTML5

HTML5 新增了一些表单提交前,浏览器会基于指定的规则进行验证,并在出错时显示适当的错误信息。而验证会基于某些条件应用到表单字段中。

必填字段

表单字段中添加 required 属性,用于标注该字段是必填项,不填则无法提交。该属性适用于<input><textarea><select>。如下所示:

<input type="text" name="account" required>

也可以通过 JavaScript 检测对应元素的 required 属性来判断表单字段是否为必填项:

let isRequired = document.forms[0].elements["account"].required;

也可以检测浏览器是否支持 required 属性:

let isRequiredSupported = "required" in document.createElement("input");

注意:不同浏览器处理必填字段的机制不同。Firefox、Chrome、IE 和Opera 会阻止表单提交并在相应字段下面显示有帮助信息的弹框,而Safari 什么也不做,也不会阻止提交表单。

更多输入类型

HTML5 为 <input> 元素增加了几个新的 type 值。如下所示:

类型

描述

number

数字值的输入

date

日期输入

color

颜色输入

range

一定范围内的值的输入

month

允许用户选择月份和年份

week

允许用户选择周和年份

time

允许用户选择时间(无时区)

datetime

允许用户选择日期和时间(有时区)

datetime-local

允许用户选择日期和时间(无时区)

email

电子邮件地址的输入

search

搜索(表现类似常规文本)

tel

电话号码的输入

url

URL地址的输入

这些输入表名字段应该输入的数据类型,并且提供了默认验证。如下所示:

<input type="email" name="email">
<input type="url" name="homepage">

要检测浏览器是否支持新类型,可以在 JavaScript 中创建 <input> 并设置 type 属性,之后读取它即可。老版本中会将我只类型设置为 text,而支持的会返回正确的值。如下所示:

let input = document.createElement("input");
input.type = "email";
let isEmailSupported = (input.type == "email");

数值范围

而上面介绍的几个如 number/range/datetime/datetime-local/date/month/week/time 几个填写数字的类型,都可以指定 min/max/step 等几个与数值有关的属性。step 属性用于规定合法数字间隔,如 step="2",则合法数字应该为 0、2、4、6,依次类推。如下所示:

<input type="number" min="0" max="100" step="5" name="count">

上面的例子是<input>中只能输入从 0 到 100 中 5 的倍数。

也可以使用 stepUp()stepDown() 方法对 <input> 元素中的值进行加减,它俩会接收一个可选参数,用于表示加减的数值。如下所示:

input.stepUp(); // 加1
input.stepUp(5); // 加5
input.stepDown(); // 减1
input.stepDown(10); // 减10

输入模式

HTML5 还为文本添加了 pattern 属性,用于指定一个正则表达式。这样就可以自己设置 <input> 元素的输入模式了。如下所示:

<input type="text" pattern="\d+" name="count">

注意模式的开头和末尾分别假设有^$。这意味着输入内容必须从头到尾都严格与模式匹配。

与新增的输入类型一样,指定 pattern 属性也不会阻止用户输入无效内容。模式会应用到值,然后浏览器会知道值是否有效。通过访问 pattern 属性可以读取模式:

let pattern = document.forms[0].elements["count"].pattern;

使用如下代码可以检测浏览器是否支持pattern 属性:

let isPatternSupported = "pattern" in document.createElement("input");

检测有效性

HTML5 新增了 checkValidity() 方法,用来检测表单中任意给定字段是否有效。而判断的条件是约束条件,因此必填字段如果没有值会被视为无效,字段值不匹配 pattern 属性也会被视为无效。如下所示:

if (document.forms[0].elements[0].checkValidity()){
    // 字段有效,继续
} else {
    // 字段无效
}

要检查整个表单是否有效,可以直接在表单上调用checkValidity()方法。这个方法会在所有字段都有效时返回true,有一个字段无效就会返回false

if(document.forms[0].checkValidity()){
    // 表单有效,继续
} else {
    // 表单无效
}

validity 属性会返回一个ValidityState 对象,表示 <input> 元素的校验状态。返回的对象包含一些列的布尔值的属性:

  • customError:如果设置了 setCustomValidity() 就返回 true,否则返回false。
  • patternMismatch:如果字段值不匹配指定的 pattern 属性则返回true。
  • rangeOverflow:如果字段值大于 max 的值则返回true。
  • rangeUnderflow:如果字段值小于 min 的值则返回true。
  • stepMisMatch:如果字段值与 minmaxstep 的值不相符则返回true。
  • tooLong:如果字段值的长度超过了 maxlength 属性指定的值则返回true。
  • typeMismatch:如果字段值不是 "email""url" 要求的格式则返回true。
  • valid:如果其他所有属性的值都为false 则返回true。与checkValidity()的条件一致。
  • valueMissing:如果字段是必填的但没有值则返回true。

因此,通过 validity 属性可以检查表单字段的有效性,从而获取更具体的信息,如下所示:

if (input.validity && !input.validity.valid){
    if (input.validity.valueMissing){
        console.log("请指定值.")
    } else if (input.validity.typeMismatch){
        console.log("请指定电子邮件地址.");
    } else {
        console.log("值无效.");
    }
}

禁用验证

通过指定 novalidate 属性可以禁止对表单进行任何验证:

<form method="post" action="/signup" novalidate>
    <!-- 表单元素 -->
</form>

也可以在 JavaScript 通过 noValidate 属性设置,为 true 表示属性存在,为 false 表示属性不存在:

document.forms[0].noValidate = true; // 关闭验证

如果一个表单中有多个提交按钮,那么可以给特定的提交按钮添加formnovalidate 属性,指定通过该按钮无需验证即可提交表单:

<form method="post" action="/foo">
    <!-- 表单元素 -->
    <input type="submit" value="注册提交">
    <input type="submit" formnovalidate name="btnNoValidate"
value="没有验证的提交按钮">
</form>

也可以使用 JavaScript 设置 formNoValidate 属性:

// 关闭验证
document.forms[0].elements["btnNoValidate"].formNoValidate = true;

总结

以上总结了 <input><textarea> 两个元素的一些功能,主要是 <input> 元素可以通过设置 type 属性获取不同类型的输入框,可以通过监听键盘事件并检测要插入的字符来控制文本框的内容。

还有一些与剪贴板相关的事件,并对剪贴的内容进行检测。还介绍了一些 HTML5 新增的属性和方法和新增的更多的 <input> 元素的类型,和一些与验证相关的属性和方法。



随着网络的广泛连接和数据的海量增长,我们面临着越来越多的安全风险。


国家有关部门为了进一步保护加强信息安全,在《中华人民共和国网络安全法》中规定,网络运营者应当按照网络安全等级保护制度的要求,履行安全保护义务,保障网络免受干扰、破坏或者未经授权的访问,防止网络数据泄露或者被窃取、篡改。


在等级保护规范《GBT 25070-2019 信息安全技术网络安全等级保护安全设计技术要求》中,明确要求用户数据完整性保护,可采用常规校验机制,检验存储的用户数据的完整性,以发现其完整性是否被破坏。


企业网站是企业的重要数字门户,网页防篡改正是为了防止网站内容被恶意篡改破坏,确保网站数据的真实性和完整性,满足等级保护用户数据完整性保护要求。


同时,网页防篡改也有助于维护企业网站声誉、保障业务正常运行、避免法律责任。


网页防篡改技术分析


网页被篡改原因分析


网页被篡改通常有以下几种方式:

  • 通过服务器漏洞: 攻击者利用服务器操作系统或应用程序(如 Web 服务器软件)的安全漏洞,获取对服务器的非法访问权限,进而修改网页文件。

  • 账号密码被窃取: 如果网站管理员或相关账号的密码强度不够或被泄露,攻击者获取到这些账号后,可以登录到后台管理系统进行网页篡改。

  • 恶意软件感染: 服务器或网站所在的系统感染了恶意软件,这些恶意软件可能会修改网页文件。

  • 网络连接被劫持: 攻击者通过网络劫持技术,在数据传输过程中篡改网页内容。

  • 内部人员篡改: 网站内部的工作人员由于各种原因,故意篡改网页内容。


要防止网页被篡改,需要 加强服务器安全防护、定期更新软件、使用强密码、加强网络安全监控 等措施。


网页防篡改主流方案


对于已经被入侵的系统,如何防止网页被篡改,主要有以下技术手段:

  • 系统文件过滤驱动: 在操作系统内核层对文件操作进行监控和过滤,实时阻止非法的文件写入和修改。

  • 事件触发机制: 设定特定事件(如文件修改事件)触发检测和保护动作。

  • 数字水印技术: 在网页中嵌入数字水印,一旦网页被篡改,水印信息会发生变化,从而被检测到。

  • 加密技术: 对网页文件进行加密,使攻击者难以直接修改加密后的内容。

  • 完整性校验技术: 如哈希算法,计算网页文件的特征值,通过对比特征值来判断是否被篡改。


本文将介绍在 Amazon Web Services 上,如何使用系统能力以及 Amazon Web Services 提供的服务能力,实现网页防篡改。


Amazon EC2 篡改防护


如果我们的网站业务直接部署于 Amazon EC2 系统中,为了确保内容不被篡改,最简单的办法就是让系统内文件只读。 但是只读后文件无法更新,所以,我们还需要对应的更新方案。


对于挂载后,我们也需要持续的监控文件是否有变化。如果有,要及时发现。


下面我们分别针对 Linux 与 Windows 系统进行介绍。


Amazon EC2 Linux 篡改防护


Amazon EC2 Linux 系统预防篡改


  • Amazon EC2 Linux 系统创建内容盘。


1.在 Amazon Web Services 控制台,我们先创建一块新的 Amazon EBS 硬盘,具体操作流程参考创建 Amazon EBS 卷。 https://docs.amazonaws.cn/zh_cn/ebs/latest/userguide/ebs-creating-volume.html


2.创建成功后,将创建的 Amazon EBS 硬盘,附加到我们的内容服务器,具体流程参考将 Amazon EBS 卷挂载到实例。 https://docs.aws.amazon.com/zh_cn/ebs/latest/userguide/ebs-attaching-volume.html


3.挂载完成后,还需要在 Linux 系统内对 Amazon EBS 卷进行分区、格式化、挂载等操作,具体流程请参考使 Amazon EBS 卷可供使用。 https://docs.amazonaws.cn/zh_cn/ebs/latest/userguide/ebs-using-volumes.html


4.之后,我们就可以使用 Amazon EBS 卷进行读写操作,编辑我们的网站内容,编辑完成后,即可对该 Amazon EBS 卷进行快照操作,创建快照的操作参考创建 Amazon EBS 快照。 https://docs.aws.amazon.com/zh_cn/ebs/latest/userguide/ebs-creating-snapshot.html


5.创建好的快照,就是我们后面网站内容的“模版”,通过该快照创建 Amazon EBS,实现内容的发布。


  • Amazon EC2 Linux 系统只读挂载。


1.通过之前创建好的快照,创建 Amazon EBS 卷,过程可以参考官方文档从快照创建卷。 https://docs.amazonaws.cn/zh_cn/ebs/latest/userguide/ebs-creating-volume.html#ebs-create-volume-from-snapshot


2.然后,与前文一致,将 Amazon EBS 卷挂载到实例。


3.挂载完成后,在 Linux 系统内,我们还需要进行加载。为了保护内容,我们采用只读挂载的方式。


1.# 确保目录存在

2.sudo mkdir -p /mnt/protect-content

3.# 确保没有挂载其他盘

4.sudo umount /mnt/protect-content

5.# `/dev/nvm1p1` 更换为实际device node

6.sudo mount -o ro /dev/nvm1p1 /mnt/protect-content




4.挂载完成,只读的内容即可供外部访问。由于是只读挂载,内容不可被修改。


  • Amazon EC2 Linux 系统内容更新。


未来,我们肯定希望对内容进行更新。更新的流程,与创建内容、只读挂载发布类似。


1.通过快照创建 Amazon EBS 卷。


2.正常可读写方式挂载到内容编辑服务器,在服务器内进行内容编辑。


3.编辑完成,重新制作 Amazon EBS 卷快照。


4.使用新的 Amazon EBS 卷快照,生成新的 Amazon EBS 卷。


5.将原来的 Amazon EBS 卷从实例分离,参考将 Amazon EBS 卷与实例分离。 https://docs.amazonaws.cn/zh_cn/ebs/latest/userguide/ebs-detaching-volume.html


6.只读方式,挂载新的 Amazon EBS 卷


7.删除原来的 Amazon EBS 卷,参考删除 Amazon EBS 卷。 https://docs.amazonaws.cn/zh_cn/ebs/latest/userguide/ebs-deleting-volume.html


Amazon EC2 Linux 系统侦测篡改事件


Inotify 是一种 Linux 内核机制,用于监视文件系统中的事件。它允许应用程序监视文件或目录的创建、删除、修改、属性更改,以及移动或重命名等事件。


Inotify 提供了一种高效且可扩展的方式,来监视文件系统活动,并使其成为各种应用程序的宝贵工具。


Inotify 有多种应用场景,包括:

  • 文件系统监控: inotify 可用于监视文件系统活动,例如文件创建、删除、修改和重命名。这对于备份应用程序、病毒扫描程序和其他需要监视文件系统活动的应用程序很有用。

  • 桌面通知: inotify 可用于在文件或目录发生更改时向用户发送桌面通知。这对于文件共享应用程序、同步工具和其他需要通知用户文件系统活动的应用程序很有用。

  • 实时事件处理: inotify 可用于实时处理文件系统事件。这对于日志记录应用程序、审计工具和其他需要实时响应文件系统活动的应用程序很有用。


Inotify 的特性,使得它在文件保护、防篡改方面,是最适合的工具。


下面的示例代码,可用于监控 Linux 文件变化,在变化的时候通知我们,让我们能第一时间反应、处理:


1.#include <stdio.h>

2.#include <stdlib.h>

3.#include <sys/inotify.h>

4.#include <unistd.h>

5.#include <fcntl.h>

6.#include <string.h>

7.

8.#define EVENT_SIZE  ( sizeof(struct inotify_event) )

9.#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )

10.

11.void handle_event(struct inotify_event *event) {

12.  if (event->len) {

13.    // Check for specific events (modify, create, delete)

14.    if (event->mask IN_MODIFY) {

15.      printf("File %s in directory was modified.\n", event->name);

16.    } else if (event->mask IN_CREATE) {

17.      printf("File %s was created in directory.\n", event->name);

18.    } else if (event->mask IN_DELETE) {

19.      printf("File %s was deleted from directory.\n", event->name);

20.    }

21.  }

22.}

23.

24.int main(int argc, char *argv[]) {

25.  if (argc != 2) {

26.    printf("Usage: %s <directory>\n", argv[0]);

27.    exit(1);

28.  }

29.

30.  // Open the inotify instance

31.  int fd = inotify_init();

32.  if (fd == -1) {

33.    perror("inotify_init");

34.    exit(1);

35.  }

36.

37.  // Add a watch on the specified directory

38.  int wd = inotify_add_watch(fd, argv[1], IN_MODIFY | IN_CREATE | IN_DELETE);

39.  if (wd == -1) {

40.    perror("inotify_add_watch");

41.    exit(1);

42.  }

43.

44.  char buffer[EVENT_BUF_LEN];

45.

46.  // Continuously read events

47.  while (1) {

48.    int nread = read(fd, buffer, EVENT_BUF_LEN);

49.    if (nread == -1) {

50.      perror("read");

51.      exit(1);

52.    }

53.

54.    int i = 0;

55.    while (i < nread) {

56.      struct inotify_event *event = (struct inotify_event *) buffer[ i ];

57.      // Check if event refers to the watched directory (avoid subdirectories)

58.      if (strcmp(event->name, "") == 0) {

59.        continue; // Skip events for the directory itself (empty name)

60.      }

61.      handle_event(event);

62.      i += EVENT_SIZE + event->len;

63.    }

64.  }

65.

66.  // Close inotify instance (would never be reached in this example)

67.  close(fd);

68.

69.  return 0;

70.}

滑动查看更多


通过编译,执行该代码,即可实现网页文件内容变更通知:


1.# 编译代码

2.gcc -o inotify inotify.c

3.# 运行监控

4../inotify <dir>




Amazon EC2 Windows 篡改防护


Amazon EC2 Windows 系统预防篡改


Amazon EC2 Windows 只读挂载防篡改与 Linux 基本一致,差异部分在于如何只读挂载 Amazon EBS 卷。


1.# 关闭自动挂载

2.mountvol.exe /N

3.# 进入分区管理

4.diskpart

5.# 列出磁盘

6.list volume

7.# 根据上面列出的,选择分区,替换<X>

8.select volume <X>

9.# 设置分区属性, readonly

10.attributes volume set readonly

11.# 查看结果

12.detail volume

滑动查看更多


其他的,与前文 Linux 系统内容类似。


Amazon EC2 Windows 系统侦测篡改事件


与 Linux 类似,Windows .NET SDK 提供了 FileSystemWatcher 这个接口。


它是 Windows 中用于监视文件系统活动的 API。它允许应用程序监视文件或目录的创建、删除、修改、重命名等事件。


我们可以参考官网的例子,编写对应的应用来监控变化。 https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher?view=net-8.0redirectedfrom=MSDN


也可以直接使用一些现有的开源工具, 比如 https://github.com/thekid/inotify-win


Inotify-win 实现了类似 Linux inotify 的功能,通过以下命令,即可实现文件目录的监控通知:


1.inotifywait.exe -r -m <dir>




Amazon S3 篡改防护


Amazon S3 网站方案


Amazon S3 简介


Amazon Simple Storage Service(Amazon S3)是一种对象存储服务,提供行业领先的可扩展性、数据可用性、安全性和性能。


各种规模和行业的客户都可以使用 Amazon S3 存储和保护任意数量的数据,用于数据湖、网站、移动应用程序、备份和恢复、归档、企业应用程序、IoT 设备和大数据分析。


可以使用 Amazon S3 托管静态网站,还可以将 Amazon S3 与内容分发网络(CDN)(如 Amazon CloudFront)结合使用来交付网页。


Amazon S3 作为 Website 先决条件


在将自定义域配置为在 Amazon Web Services 中国(宁夏)区域托管 Amazon S3 静态网站之前,必须满足以下先决条件。


取得 ICP 备案


如果域名通过 Amazon Web Services 中国(宁夏)区域中,由宁夏西云数据科技有限公司(NWCD)运营的服务器进行公共访问,必须通过 NWCD 申请 ICP 备案。


配置 Amazon S3 静态网站


1.创建域存储桶和可选的子域存储桶:


  • 对于域存储桶,启用静态网站托管,然后上传网站内容。启用静态网站托管后,域存储桶会被分配一个 Amazon S3 网站端点,例如 www.example.com.cn.s3-website.cn-north-1.amazonaws.com.cn,使用此网站端点配置 Amazon Route 53 自定义域。

  • 对于子域存储桶,在静态网站托管下,为对象配置重定向请求,以重定向至上传到域存储桶的网站内容。


域存储桶包含构成网站的文件,子域存储桶会将请求重新路由到域存储桶。例如,如果客户进入 www.example.com.cn,子域存储桶会将请求转发到域存储桶:example.com.cn。


2.清除所有四种 Amazon S3 屏蔽公共访问权限设置,以便可以添加允许对存储桶进行公共读取访问的存储桶策略。


3.添加允许对存储桶进行公共读取访问的存储桶策略,将网站暴露给外部进行访问。


使用 Amazon Route 53 配置自定义域


完成在 Amazon S3 中配置静态网站后,就有了网站端点,假设为 www.example.com.cn.s3-website.cn-north-1.amazonaws.com.cn(www.example.com.cn),可以将自定义域名映射到此端点,并通过自定义域访问网站。


要为域和子域配置别名记录,请按照以下步骤操作:


1.通过以下网址打开 Amazon Route 53 控制台: https://console.aws.amazon.com/route53/


2.请选择托管区域。


3.在托管区域列表中,请选择与域名匹配的托管区域的名称。


4.要为托管区创建别名记录,请选择创建记录。



5.请选择切换到向导。



6.选择简单路由,然后选择下一步。


7.选择定义简单记录。



  • 对于记录名称,接受默认值。

  • 对于记录类型,选择 CNAME 将流量路由到另一个域名和某些 Amazon Web Services 资源。

  • 对于值/流量路由至,选择根据记录类型选择 IP 地址或其他值。在文本框中,输入 Amazon S3 网站端点:s3-website.cn-north-1.amazonaws.com.cn。

  • 选择定义简单记录。


8.通过自定义域名(例如 http://example.com.cn)访问网站,以验证网站和重定向是否有效。


通过 Amazon CloudFront 建立加速缓存


Amazon CloudFront 是 Amazon Web Services 上的 CDN 服务,它可以将 Amazon S3 设置为分发的对象,将 Amazon S3 上的资源,通过 Amazon Web Services 分布在边缘节点将内容提供给用户。


同时,Amazon CloudFront 还提供了 Amazon S3 不支持的 HTTPS 协议,让前端应用更加安全。然而由于 Amazon Web Services 中国(宁夏)区域的一些特殊情况,在此使用 Amazon S3 + Amazon CloudFront,需要一些不同于 Global 的额外配置。


1. 建立 Amazon S3 数据桶。


注意:需要设置阻止公开访问。



2. 建立 Amazon Route 53。


红框内输入已备案的域名。



3. 建立 Amazon CloudFront 分配。


源选择存放前端资源的 Amazon S3 桶,桶中如果没有子文件夹,则留空源路径输入框,来源访问选择“遗留访问身份”,点击“建立新的 OAI”,下面选择“否,我将更新存储桶策略”。



在备用域名中,添加刚才在 Amazon Route 53 添加的域名,默认根对象填写 index.html (根 HTML 文件文件名)。



建立完成后,进入“源”标签页,记下 Origin access 的值,如图红框所示:



4. 更新 Amazon S3 桶策略并保存。



代码如下(替换{$ORIGIN_ACCESS_ID}和BUCKET_ARN/*):


1.{

2.    "Version": "2012-10-17",

3.    "Statement": [

4.        {

5.            "Effect": "Allow",

6.            "Principal": {

7.                "AWS": "arn:aws-cn:iam::cloudfront:user/CloudFront Origin Access Identity {$ORIGIN_ACCESS_ID}"

8.            },

9.            "Action": "s3:GetObject",

10.            "Resource": “BUCKET_ARN/*"

11.        }

12.    ]

13.}

滑动查看更多


5. 回到 Amazon Route 53,增加别名。



如果实用子域名,则需要新增 CNAME 记录,并在 Amazon CloudFront 的备用域名中,输入该记录的完整域名。



全部完成后,等待片刻,即可去设定的域名测试效果。


Amazon S3 网站预防内容篡改


Amazon S3 对象版本


Amazon S3 中的版本控制,是在相同的存储桶中保留对象的多个版本的方法。对于存储桶中存储的每个对象,可以使用 Amazon S3 版本控制功能来保留、检索和还原它们的各个版本,从而达到防止被篡改的效果。


启用了版本控制的存储桶,可以恢复因意外或被外部恶意删除操作而被篡改的对象。例如,如果删除对象,Amazon S3 会插入删除标记,而不是永久删除该对象。


存储桶拥有者和所有获得授权的 Amazon IAM 用户,都可以启用版本控制。


  • 版本 ID


如果为存储桶启用版本控制,Amazon S3 会自动为要存储的对象生成唯一版本 ID。例如,在一个存储桶中,可以拥有两个具有相同键(对象名称)的对象,但版本 ID 不同,例如 photo.gif(版本 111111)和 photo.gif(版本 121212)。



无论 Amazon S3 版本控制是否启用,每个对象都有一个版本 ID。


如果未启用 Amazon S3 版本控制,Amazon S3 将版本 ID 的值设置为 null。如果启用 Amazon S3 版本控制,Amazon S3 会为对象分配版本 ID 值。此值将该对象与同一个键的其他版本区分开来。


在现有存储桶上启用 Amazon S3 版本控制时,已存储在存储桶中的对象将保持不变。版本 ID(null)、内容和权限保持不变。启用 Amazon S3 版本控制后,添加到存储桶的每个对象都会获得一个版本 ID,该 ID 将此版本与同一个键的其他版本区分开来。


版本控制工作流程


当在启用版本控制的存储桶中,通过 PUT 放入对象时,不会覆盖非当前版本。如下图所示,当将 photo.gif 的新版本 PUT 到一个已经包含同名对象的存储桶中时,会发生以下行为:

  • 原始对象(ID = 111111)保留在存储桶中。

  • Amazon S3 生成一个新的版本 ID(121212),并将这个较新版本的对象添加到存储桶中。



使用此功能,如果对象被意外覆盖或删除,则可以检索对象的先前版本。


当 DELETE 对象时,所有版本都将保留在存储桶中,而 Amazon S3 将插入删除标记,如下图所示:



删除标记将成为对象的当前版本。默认情况下,GET 请求将检索最新存储的版本。在当前版本为删除标记时,执行 GET Object 请求将返回 404 Not Found 错误,如下图所示:



但是,可以通过指定对象版本 ID,通过 GET 获取非当前版本的对象。在下图中,GET 特定对象版本 111111。即使该对象版本不是当前版本,Amazon S3 也会返回它。


有关更多信息,请参阅从启用了版本控制的存储桶中检索对象版本。 https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/RetrievingObjectVersions.html



可以通过指定要删除的版本来永久删除对象。只有 Amazon S3 存储桶的拥有者或者授权的 Amazon IAM 用户,才能永久删除某个版本。如果 DELETE 操作指定了 versionId,则会永久删除该对象版本,Amazon S3 不会插入删除标记。



可以通过配置存储桶,来启用多重身份验证(MFA)删除,从而提升安全性。 对存储桶启用 MFA 删除时,存储桶拥有者,必须在任何请求中包含两种形式的身份验证,以删除版本或更改存储桶的版本控制状态。


在存储桶上启用版本控制


在 Amazon S3 存储桶上,启用或禁用版本控制:


1.登录到 Amazon Web Services Management Console。


2.在存储桶列表中,请选择要为其启用版本控制的存储桶的名称。


3.请选择属性。


4.在存储桶版本控制下,请选择编辑。


5.请选择 Suspend (暂停) 或 Enable (启用),然后选择 Save changes (保存更改)。


Amazon S3 使用对象锁


  • 一次写入多次读


Amazon S3 Object Lock 是 Amazon S3 的一项功能, 允许使用一次写入、多次读取(WORM)模式存储对象, 可以在数据写入后不得更改或删除的情况下,使用 WORM 保护,使用此功能无需支付额外费用。


Amazon S3 Object Lock 提供两种管理对象保留的方法, 第一种是保留期,第二种是合法保留。


保留期规定了对象保持锁定状态的固定时间段。在此期间,对象受 WORM 保护,不能被覆盖或删除。保留期的单位可以是天数,也可以是年数,最短为 1 天,没有最长限制。


合法保留提供的保护与保留期相同,但没有过期日期。相反,合法保留会一直存在,直到明确将其移除。


使用 Amazon S3 对象锁,可以防止对象在固定时间内被删除或覆盖,或直到合法保留被移除。一个对象版本可以同时具有保留期和合法保留。


Amazon S3 侦测对象内容变更


在对对象做了相应的安全保护手段之后,最后我们还需要具备侦测的能力,即当对象真的被篡改之后,可以收到相应的通知,以及时采取后续策略。


我们可以利用 Amazon S3 的事件功能结合 Amazon SNS 服务,将通知发送到运维或相关人员的邮箱、微信等其他应用。


目前, Amazon S3 可以发布用于以下事件的通知:

  • 新的对象创建事件

  • 对象移除事件

  • 还原对象事件

  • 低冗余存储 (RRS) 对象丢失事件

  • 复制事件

  • Amazon S3 生命周期到期事件

  • Amazon S3 生命周期转换事件

  • Amazon S3 Intelligent-Tiering 自动归档事件

  • 对象标记事件

  • 对象 ACL PUT 事件


本文以对象被移除并发布邮件通知为例进行说明。


对象内容变化监控架构:



当托管在 Amazon S3 当中的静态网页被移除时(篡改),配置好的 Amazon S3 事件会侦测到这个动作,并且通过 Amazon SNS 服务将通知发送给订阅好的邮箱。


实现过程


1.上传托管的静态文件。



2.创建 Amazon SNS 通知主题。



这里需要注意的是,为了让 Amazon S3 event 具有可以调用 Amazon SNS API 的权限,需要配置 topic 的 access policy,按照如下样例进行配置:


1.{

2.    "Version": "2012-10-17",

3.    "Id": "example-ID",

4.    "Statement": [

5.        {

6.            "Sid": "Example SNS topic policy",

7.            "Effect": "Allow",

8.            "Principal": {

9.                "Service": "s3.amazonaws.com"

10.            },

11.            "Action": [

12.                "SNS:Publish"

13.            ],

14.            "Resource": "SNS-topic-ARN",

15.            "Condition": {

16.                "ArnLike": {

17.                    "aws:SourceArn": "arn:aws:s3:*:*:bucket-name"

18.                },

19.                "StringEquals": {

20.                    "aws:SourceAccount": "bucket-owner-account-id"

21.                }

22.            }

23.        }

24.    ]

25.}

滑动查看更多


3.使用邮箱订阅主题,完成后的邮箱会收到邮件确认,点击链接后完成订阅主题。



4.在属性标签配置 Amazon S3 事件,创建事件通知。



5.配置事件通知参数。



选择之前创建的 Amazon SNS 主题。




6.测试删除文件后,是否能捕获通知。




总结


本文介绍在 Amazon Web Services 上,如何在 Amazon EC2 Linux/Windows 系统、Amazon S3 网站托管,实现网页防篡改。


在 Amazon EC2 系统中,可以通过 Amazon EBS Snapshot 只读挂载实现,同时,通过 inotify 机制实现监控;在 Amazon S3,可以通过对象锁、对象版本实现只读,通过 Amazon Lambda 实现 Amazon S3 变更事件的通知与自动化处理。