如何在浏览器自动化中处理Web身份验证
Senior Cybersecurity Analyst
TL;DR:
- 浏览器自动化身份验证应重用已批准的会话,而不是为每个作业重复登录。 保存的状态可以将 cookies 和来源存储带入一个新的浏览器上下文。
- 身份验证证明身份;授权决定该身份可以做什么。 成功的登录永远不会扩展自动化的批准范围。
- 会话文件是凭证。 将它们排除在源控制之外,对其静态加密,限制其生命周期,并按帐户和环境孤立它们。
- MFA、密码钥匙、同意屏幕和设备批准创建了人际边界。 通过批准的操作流程完成这些步骤,然后仅重用结果授权的会话。
- Scrapeless Scraping Browser 可以在云浏览器中保持授权的浏览器会话。 您的应用程序仍然拥有凭证处理、帐户政策和访问决策。
- 免费开始。 新的 Scrapeless 帐户包括免费的 Scraping Browser 运行时 — 在 app.scrapeless.com 注册。
介绍:身份验证状态是安全边界
当网站接受身份证明并将该身份绑定到浏览器状态时,浏览器会话变得经过身份验证。该状态可以保存在 HttpOnly cookie、中间存储、内存令牌或在重定向过程中设置的几个协调值中。
当自动化将该状态视为填写表单的问题时,它会失败。重复登录会增加凭证暴露、重复 MFA 提示,并隐藏过期会话和页面缺陷之间的差别。更安全的设计只需创建一次授权状态,验证其范围,并为每个作业提供一个仅用该状态填充的新浏览器上下文。
本指南使用 Playwright 演示本地测试应用程序上的状态边界,然后显示相同的授权工作流程如何连接到 Scrapeless Scraping Browser。仅与您拥有或明确授权自动化的帐户和系统一起使用。
身份验证与授权
身份验证回答“哪个身份正在使用这个浏览器?” 授权回答“该身份可以访问哪些资源和操作?” 浏览器自动化需要这两项检查。
| 层级 | 问题 | 自动化控制 |
|---|---|---|
| 身份验证 | 身份得到证明了吗? | 验证登录后的 URL 和帐户特定元素 |
| 会话 | 证明仍然绑定到这个上下文吗? | 检查 cookie 范围、存储状态和到期行为 |
| 授权 | 该身份可以执行请求的操作吗? | 使用专用角色和明确的操作白名单 |
| 审计 | 操作可以追踪吗? | 记录作业、帐户别名、批准的目的和结果 |
OAuth 是一种授权框架,即使其重定向通常出现在登录过程中。 OAuth 2.0 定义了角色和授权授予流程;浏览器不应将授权代码或访问令牌暴露给日志。
浏览器会话如何保持身份
Cookies 是最常见的浏览器侧会话承载者。服务器发出 cookie,浏览器应用其域、路径、到期和安全属性,后续请求在范围匹配时包含它。 HTTP 状态管理规范定义了 cookie 存储和匹配。
应用程序也可以在 localStorage 或 IndexedDB 中存储令牌或帐户提示。 sessionStorage 是不同的:它属于一个顶级浏览上下文,并且不会通过正常的存储状态导出自动复制。在设计重用之前识别真实的状态表面。
JWT 描述了一种令牌格式,而不是浏览器会话策略。JWT 可以出现在 cookie 中、来源存储中,或仅在应用程序内存中。将包含机制视为安全边界。
密码、会话、OAuth 和 WebAuthn
每种身份验证方法都创建了不同的自动化边界。
| 方法 | 自动化可以安全拥有的内容 | 应该保留在脚本之外的内容 |
|---|---|---|
| 密码表单 | 一个专用的测试帐户和在运行时提供的密码 | 个人凭证和硬编码密码 |
| 会话 cookie | 一个短期的、加密的状态产物 | 其他用户的 cookie 或无限制的生产会话 |
| OAuth/OIDC | 针对测试租户的批准重定向和回调 | 提供者凭证、超出批准范围的同意 |
| MFA | 操作员批准后的后 MFA 会话 | 短信、TOTP、推送或硬件密钥拦截 |
| WebAuthn/密码钥匙 | 在拥有的环境中控制的测试验证器 | 个人的生物识别或设备绑定的私钥 |
| WebAuthn使用公钥凭证,其范围指定为依赖方。 Web身份验证规范定义了浏览器和身份验证器的过程。生产环境下的通行密钥或安全密钥故意难以导出,因此自动化计划应该保持这一边界。 |
安装测试工具
执行的示例使用Node.js和Playwright。安装与验证运行时使用的相同软件包版本:
bash
npm install playwright@1.62.1
为状态文件使用专用的 .auth 目录,并将其排除在版本控制之外。状态文件在有效期内可能足以冒充测试账户。
安全重用授权会话
以下脚本创建一个带有假测试账户的本地应用,登录一次,保存浏览器状态,并从新上下文打开受保护的路由。它不接触第三方登录服务,也不使用真实凭证。
javascript
import http from "node:http";
import fs from "node:fs/promises";
import { chromium } from "playwright";
const server = http.createServer(async (request, response) => {
const url = new URL(request.url, "http://127.0.0.1");
const cookies = request.headers.cookie ?? "";
if (request.method === "POST" && url.pathname === "/login") {
let body = "";
for await (const chunk of request) body += chunk;
const form = new URLSearchParams(body);
if (form.get("username") === "test-user" && form.get("password") === "test-password") {
response.writeHead(302, {
"Set-Cookie": "demo_session=authorized; HttpOnly; SameSite=Lax; Path=/",
Location: "/account",
});
response.end();
return;
}
}
if (url.pathname === "/account") {
const authorized = cookies.includes("demo_session=authorized");
response.writeHead(authorized ? 200 : 401, { "Content-Type": "text/html" });
response.end(`<title>${authorized ? "Authorized account" : "Sign in required"}</title>`);
return;
}
response.writeHead(200, { "Content-Type": "text/html" });
response.end(`<form method="post" action="/login">
<label>Username <input name="username"></label>
<label>Password <input name="password" type="password"></label>
<button>Sign in</button>
</form>`);
});
await new Promise(resolve => server.listen(0, "127.0.0.1", resolve));
const address = server.address();
const baseUrl = `http://127.0.0.1:${address.port}`;
const statePath = "authorized-state.json";
const browser = await chromium.launch({
headless: true,
executablePath: process.env.CHROME_PATH || undefined,
});
try {
const context = await browser.newContext();
const page = await context.newPage();
await page.goto(baseUrl);
await page.getByLabel("Username").fill("test-user");
await page.getByLabel("Password").fill("test-password");
await Promise.all([
page.waitForURL(`${baseUrl}/account`),
page.getByRole("button", { name: "Sign in" }).click(),
]);
await context.storageState({ path: statePath });
await context.close();
const restored = await browser.newContext({ storageState: statePath });
const restoredPage = await restored.newPage();
const result = await restoredPage.goto(`${baseUrl}/account`);
const state = JSON.parse(await fs.readFile(statePath, "utf8"));
console.log(JSON.stringify({
status: result.status(),
title: await restoredPage.title(),
cookieNames: state.cookies.map(cookie => cookie.name),
}));
await restored.close();
} finally {
await browser.close();
server.close();
await fs.rm(statePath, { force: true });
}
实时运行返回HTTP 200,标题Authorized account,以及一个名为demo_session的cookie。这证明恢复的上下文在不重复登录的情况下接收了授权状态。
开始使用Scrapeless抓取
利用Scrapeless增强您的网络抓取和自动化工作流程!
今天注册,获得5美元的免费信用 — 无需信用卡。立即在Scrapeless Dashboard领取您的免费信用。
MFA和通行密钥边界
多因素认证证明了某个已批准的人或设备的存在。自动化不应削弱这一证明。
对于拥有的测试系统,使用身份提供商测试租户、专用账户和文档化的虚拟身份验证器。对于生产访问,让操作员通过正常界面完成MFA、通行密钥、同意或设备批准步骤。然后,工作流程可以在政策定义的到期之前重用已发行的会话。
请勿收集某人的一次性代码,复制设备绑定的通行密钥,或压制同意屏幕。OWASP会话管理指南解释了会话标识符为何需要与身份验证凭证一样的谨慎对待。
使用Scrapeless抓取浏览器实现流程
Scrapeless抓取浏览器将在受管理的云浏览器中移动浏览器进程,同时您的Playwright代码保持对导航和状态的控制。
在Playwright旁边安装SDK:
bash
npm install @scrapeless-ai/sdk@1.11.0 playwright@1.62.1
注意:以下代码块需要您的Scrapeless API密钥和您被授权自动化的账户。凭证无关的验证环境加载了确切的SDK,并确认
Playwright.connect是一个函数,但无法创建已验证的云会话。
javascript
import { Playwright } from "@scrapeless-ai/sdk";
const browser = await Playwright.connect({
sessionName: "authorized-workflow",
sessionTTL: 300,
proxyCountry: "US",
});
const context = browser.contexts()[0];
const page = await context.newPage();
await page.goto("https://app.example.com/login", { waitUntil: "domcontentloaded" });
// Complete only the login steps approved for this account.
// An operator handles any MFA, passkey, consent, or device-approval boundary.
await page.waitForURL("https://app.example.com/account");
const state = await context.storageState();
console.log(JSON.stringify({ cookieCount: state.cookies.length, originCount: state.origins.length }));
await browser.close();
Scrapeless抓取浏览器文档涵盖了API密钥设置和会话创建。 抓取浏览器产品页面和定价描述了受管理的浏览器表面。
调试身份验证状态
从外部调试身份状态。从最终URL和可见账户标记开始,然后检查应支持该状态的浏览器存储。
| 症状 | 检查 | 安全操作 |
|---|---|---|
| 登录表单再次出现 | Cookie过期、域名、路径和重定向完成 | 通过正常登录生成新的批准状态 |
受保护页面返回401或403 |
身份有效性和角色权限 | 确认账户已被授权;不要扩大角色 |
| Cookie存在但应用看起来已退出登录 | 来源存储、IndexedDB或服务器端会话 | 捕获拥有的应用的完整状态表面 |
| 状态在本地有效但在其他区域无效 | 区域绑定政策或风险控制 | 钉住批准的区域并记录约束 |
| 一个工作者影响另一个 | 共享账户或共享上下文 | 通过工作流程隔离上下文和账户 |
| Playwright代理和云浏览器指南 提供了在区域和浏览器基础设施需要移出应用程序主机时的下一步。 |
安全和合规检查清单
- 仅使用工作流被授权访问的系统、租户和帐户。
- 为自动化帐户分配可以完成批准任务的最小角色。
- 在运行时提供密码和密钥;切勿将它们放入源代码或截图中。
- 将cookie、存储状态文件、授权码和令牌视为机密。
- 加密会话文档,限制文件权限,并在策略到期时删除它们。
- 将开发、预发布和生产身份分开。
- 对MFA、同意、财务行动、权限更改和破坏性操作要求人工批准。
- 记录目的、帐户别名、目标来源和结果,不要记录机密值。
结论:重用证明,而非凭证
可靠的浏览器自动化创建一个狭窄的授权会话,对其进行验证,并在孤立上下文中重用该证明。它不会在每个页面上重复凭证或将成功的身份验证视为每个操作的权限。
保持会话文档短暂而受保护。让人们完成设计给人类的安全仪式。当批准的工作流需要一个受管理的浏览器会话,而不将授权边界移动到云提供商时,请使用Scrapeless Scraping Browser。
准备构建授权浏览器工作流了吗?
加入我们的社区以申请免费计划,并与构建受控浏览器自动化的开发人员联系: Discord · Telegram。
在 app.scrapeless.com 免费注册Scraping Browser运行时,并将状态隔离模式应用于您被授权自动化的帐户。
常见问题
问:自动化一个经过身份验证的网站是否合法?
授权的自动化可以是合法的,但答案取决于管辖区、合同、数据和行为。使用拥有的或明确批准的帐户,审查网站的条款和隐私规则,并咨询法律顾问以获取特定工作流的信息。
问:浏览器自动化应该存储密码还是会话?
浏览器自动化通常应该在运行时接收机密,创建一个短暂的授权会话,并重用受保护的状态。存储的会话仍然是凭证,需进行加密、访问控制、到期和删除。
问:自动化能处理MFA或密码钥匙吗?
自动化可以在拥有的测试环境中使用测试身份验证器,但生产环境中的MFA、密码钥匙、同意或设备批准步骤应仍由授权操作员负责。在其批准范围内重用生成的会话。
问:经过身份验证的工作流需要代理吗?
当应用程序将风险策略或内容绑定到位置时,经过身份验证的工作流可能需要稳定的区域出口。将批准的国家固定为会话,并且不要在一个身份流中更改区域。
问:当标记更改时应该发生什么?
重新检查基于角色的定位器和登录后的断言。身份验证状态和DOM选择器是不同的关注点;有效会话可以与更改的界面共存。
问:一个经过身份验证的帐户应该使用多少并发?
每个主机最多保持三个工作者,除非应用程序所有者批准另一个限制,当并行作业可能更改共享服务器端状态时,请使用不同的帐户。
问:这个可以在没有AI代理的情况下运行吗?
可以。Playwright和Scrapeless SDK可以直接运行整个授权流程。AI代理是可选的,应在相同的帐户、操作和批准边界内工作。
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。




