84. 保护网页免受点击劫持的Content Security Policy(CSP)
什么是点击劫持?
点击劫持(Clickjacking)是一种网络安全攻击技术,通过在一个网页上放置透明的或误导性的内容,诱使用户在不知情的情况下点击恶意操作。这种攻击技术可以欺骗用户并执行他们不想执行的操作,如点击隐藏的按钮或链接,可能导致敏感信息泄露、支付信息被篡改等风险。
Content Security Policy(CSP)的作用
Content Security Policy(CSP)是一种用于增强网页安全性的安全策略。它通过定义可信任的资源源和加载行为,限制了网页中的内容来源,从而防止点击劫持等攻击。CSP提供了一系列的指令,允许网页开发者明确控制浏览器加载和执行的资源。
设置CSP策略
要设置CSP策略,可以通过添加<meta>
标签或响应头的方式来告知浏览器该网页的安全策略。
使用<meta>
标签设置CSP策略
在网页的头部添加以下<meta>
标签来设置CSP策略:
<meta http-equiv="Content-Security-Policy" content="指令1; 指令2; ...">
具体的CSP指令根据实际需求进行配置。以下是一个简单的示例,只允许加载同源的资源:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
上述示例中,default-src
指令设置了默认策略,其中'self'
表示只允许加载同源的资源。这样可以防止网页被其他域名的恶意页面嵌套,减少点击劫持的风险。
使用响应头设置CSP策略
在服务器端,可以通过设置响应头来指定CSP策略。以下是一个Node.js Express框架的示例:
const express = require('express');
const app = express();
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy', "default-src 'self'");
next();
});
// 网页路由和其他逻辑...
app.listen(3000, () => {
console.log('服务器已启动,监听端口 3000');
});
上述示例中,我们使用Express框架在每个请求的响应中设置了Content-Security-Policy
头部,将默认策略设置为只允许加载同源的资源。
通过根据实际需求设置适当的CSP策略,我们可以有效地保护网页免受点击劫持等攻击的影响,提升网页的安全性。
每日一游 - 大型端游之俄罗斯方块
<!DOCTYPE html>
<html>
<head>
<title>俄罗斯方块</title>
<style>
.block {
width: 29px;
height: 29px;
border: 1px solid #000;
position: absolute;
}
body {
background-color: #f8f9fa;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
font-family: Arial, sans-serif;
}
#game-area {
width: 300px;
height: 600px;
margin: 0 auto;
position: relative;
background-color: #fff;
border: 4px solid #ced4da;
border-radius: 8px;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
}
#score {
text-align: center;
font-size: 24px;
margin-top: 16px;
}
</style>
</head>
<body>
<h1>俄罗斯方块</h1>
<div id="game-area"></div>
<div id="score">Score: 0</div>
<button id="start-button">Start</button>
<script>
// 计分系统
let gameStarted = false;
function startGame() {
if (!gameStarted) {
gameStarted = true;
score = 0;
grid = [];
for (let i = 0; i < 20; i++) {
grid.push(new Array(10).fill(0));
}
clearGameArea();
generateNewTetromino();
gameInterval = setInterval(moveDown, 1000);
document.getElementById('start-button').disabled = true;
}
}
function restartGame() {
score = 0;
grid = [];
for (let i = 0; i < 20; i++) {
grid.push(new Array(10).fill(0));
}
clearGameArea();
generateNewTetromino();
gameInterval = setInterval(moveDown, 1000);
document.getElementById('start-button').disabled = true;
}
document.getElementById('start-button').addEventListener('click', startGame);
let gameInterval;
const gameArea = document.getElementById('game-area');
const tetrominoShapes = [
[
[1, 1, 1, 1],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[1, 1],
[1, 1]
],
[
[1, 1, 1],
[0, 1, 0]
],
[
[ 1, 1 ],
[ 0, 1 ],
[ 0, 1 ],
],
// 添加更多方块形状...
];
let currentTetromino = tetrominoShapes[0]; // 初始方块形状
let currentX = 0; // 初始方块横向位置
let currentY = 0; // 初始方块纵向位置
function createBlock() {
const block = document.createElement('div');
block.className = 'block';
return block;
}
function drawBlock(x, y) {
const block = createBlock();
block.style.left = x + 'px';
block.style.top = y + 'px';
gameArea.appendChild(block);
}
function clearGameArea() {
while (gameArea.firstChild) {
gameArea.removeChild(gameArea.firstChild);
}
}
function drawTetromino() {
clearGameArea();
for (let i = 0; i < grid.length; i++) {
const row = grid[i];
for (let j = 0; j < row.length; j++) {
if (row[j]) {
const x = j * 30;
const y = i * 30;
drawBlock(x, y);
}
}
}
for (let i = 0; i < currentTetromino.length; i++) {
const row = currentTetromino[i];
for (let j = 0; j < row.length; j++) {
if (row[j]) {
const x = (j + currentX) * 30;
const y = (i + currentY) * 30;
drawBlock(x, y);
}
}
}
}
function moveDown() {
if (!checkCollision(0, 1, currentTetromino)) {
currentY++;
drawTetromino();
} else {
mergeTetromino();
clearFullRows();
generateNewTetromino();
checkGameOver();
}
}
function moveLeft() {
if (!checkCollision(-1, 0, currentTetromino)) {
currentX--;
drawTetromino();
}
}
function moveRight() {
if (!checkCollision(1, 0, currentTetromino)) {
currentX++;
drawTetromino();
}
}
function rotateTetromino() {
const rotatedTetromino = [];
for (let i = 0; i < currentTetromino[0].length; i++) {
const newRow = [];
for (let j = currentTetromino.length - 1; j >= 0; j--) {
newRow.push(currentTetromino[j][i]);
}
rotatedTetromino.push(newRow);
}
if (!checkCollision(0, 0, rotatedTetromino)) {
currentTetromino = rotatedTetromino;
drawTetromino();
}
}
function checkCollision(offsetX, offsetY, tetromino) {
for (let i = 0; i < tetromino.length; i++) {
for (let j = 0; j < tetromino[i].length; j++) {
if (tetromino[i][j]) {
const newX = j + currentX + offsetX;
const newY = i + currentY + offsetY;
if (newX < 0 || newX >= 10 || newY >= 20 || (newY >= 0 && grid[newY][newX])) {
return true;
}
}
}
}
return false;
}
function mergeTetromino() {
for (let i = 0; i < currentTetromino.length; i++) {
for (let j = 0; j < currentTetromino[i].length; j++) {
if (currentTetromino[i][j]) {
const x = j + currentX;
const y = i + currentY;
grid[y][x] = 1;
}
}
}
}
function generateNewTetromino() {
currentTetromino = tetrominoShapes[Math.floor(Math.random() * tetrominoShapes.length)];
currentX = Math.floor((10 - currentTetromino[0].length) / 2);
currentY = 0;
}
function clearFullRows() {
const fullRows = [];
for (let y = 0; y < grid.length; y++) {
if (grid[y].every(cell => cell === 1)) {
fullRows.push(y);
}
}
for (let i = 0; i < fullRows.length; i++) {
const row = fullRows[i];
grid.splice(row, 1);
grid.unshift(new Array(10).fill(0));
}
}
let grid = [];
for (let i = 0; i < 20; i++) {
grid.push(new Array(10).fill(0));
}
gameInterval = setInterval(moveDown, 1000);
document.addEventListener('keydown', (event) => {
if (event.code === 'ArrowLeft') {
moveLeft();
} else if (event.code === 'ArrowRight') {
moveRight();
} else if (event.code === 'ArrowUp') {
rotateTetromino();
} else if (event.code === 'ArrowDown') {
moveDown()
}
});
function checkGameOver() {
const topRow = grid[0];
if (topRow.some(cell => cell === 1)) {
clearInterval(gameInterval);
alert('游戏结束! 你的分数为: ' + score);
}
}
function updateScore(linesCleared) {
score += linesCleared * 10;
}
function clearGameArea() {
while (gameArea.firstChild) {
gameArea.removeChild(gameArea.firstChild);
}
}
function drawTetromino() {
clearGameArea();
for (let i = 0; i < grid.length; i++) {
const row = grid[i];
for (let j = 0; j < row.length; j++) {
if (row[j]) {
const x = j * 30;
const y = i * 30;
drawBlock(x, y);
}
}
}
for (let i = 0; i < currentTetromino.length; i++) {
const row = currentTetromino[i];
for (let j = 0; j < row.length; j++) {
if (row[j]) {
const x = (j + currentX) * 30;
const y = (i + currentY) * 30;
drawBlock(x, y);
}
}
}
}
function mergeTetromino() {
for (let i = 0; i < currentTetromino.length; i++) {
for (let j = 0; j < currentTetromino[i].length; j++) {
if (currentTetromino[i][j]) {
const x = j + currentX;
const y = i + currentY;
grid[y][x] = 1;
}
}
}
checkGameOver();
}
function clearFullRows() {
const fullRows = [];
for (let y = 0; y < grid.length; y++) {
if (grid[y].every(cell => cell === 1)) {
fullRows.push(y);
}
}
for (let i = 0; i < fullRows.length; i++) {
const row = fullRows[i];
grid.splice(row, 1);
grid.unshift(new Array(10).fill(0));
}
updateScore(fullRows.length);
}
let score = 0;
drawTetromino();
</script>
</body>
</html>