我有一个常用的 Symfony 应用程序,包括防火墙、用户提供程序和通过表单登录 (FOSUserBundle)、自定义令牌身份验证(对于应用程序),甚至是应用程序一部分的 http 基本身份验证。当然一次只使用其中一个。特定区域由连接到这些提供商的不同防火墙保护。
security.yml
的提取物
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
providers:
authentication_provider:
entity:
class: MyAppBundle:User
property: apiKey
fos_userbundle:
id: fos_user.user_provider.username
...
通常在实时系统上没有
app_dev.php
,因为这是一个巨大的安全漏洞。但如果需要,它也是在实时系统上进行调试的好方法。我的想法是通过额外的 http 基本身份验证来保护开发环境。这样开发环境仍然是安全的,但您可以在需要时访问它。
但不幸的是我无法让它工作,因为一旦你将 http 基本身份验证添加到一般站点并登录,你的用户就通过了 Symfony 应用程序的身份验证。这当然是首先进行身份验证的漏洞点,但是这个想法的交易破坏者因为应用程序无法区分表单登录用户和基本身份验证用户(我知道我可以区分用户类型但是这将是一个额外的复杂层,我不愿意添加,因为它必须在孔应用程序中检查)。
我的问题是是否可以在没有身份验证部分的情况下拥有 Symfony 本机 http 基本身份验证。因此,在完成基本身份验证后,浏览器仍在发送 http 基本身份验证,但您仍然是匿名的,并且仍然必须通过表单(例如)登录以进行身份验证并可以访问其他受限内容。
编辑:我知道有一些方法可以像在 Apache 中那样在 Symfony 之外的服务器配置中配置它,但我正在寻找一个 Symfony 解决方案(无需托管更改即可工作,包括本地,可以推送到源代码管理,.. .)
编辑 2:经过深入研究,我发现所有身份验证过程都必须返回某种令牌。所以它可能不像覆盖提供者并返回 Anonymus 令牌那么简单。
配置方法如下:
security:
providers:
api_users_in_memory:
memory:
users:
test:
password: qwerty
encoders:
Symfony\Component\Security\Core\User\User: plaintext
firewalls:
api:
pattern: ^/api
stateless: true
http_basic:
provider: api_users_in_memory
更新版本的@sam-dark (https://stackoverflow.com/a/49238124/21333278) 用于 Symfony 6。由于“编码器”消失了。
security:
password_hashers:
Symfony\Component\Security\Core\User\InMemoryUser: 'plaintext'
# ...
providers:
users_in_memory: { memory: null }
http_basic_user_provider_name:
memory:
users:
'%env(HTTP_BASIC_AUTH_USERNAME)%': { password: '%env(HTTP_BASIC_AUTH_PASSWORD)%', roles: ['ROLE_EXAMPLE'] }
firewalls:
http_basic_area_name:
pattern: ^/your_basic_routes_patterns_here
http_basic:
provider: http_basic_user_provider_name
# ...
access_control:
- { path: ^/your_basic_routes_patterns_here, roles: ROLE_EXAMPLE }
# ...
其中 HTTP_BASIC_AUTH_USERNAME、HTTP_BASIC_AUTH_PASSWORD 是来自 .env 的普通用户/密码值。
如果你想使用加密的密码值,只需删除
Symfony\Component\Security\Core\User\InMemoryUser: 'plaintext'
并通过网站上使用的哈希算法设置密码值。默认
php bin/console security:hash-password
显然,由于漏洞安全组件的构建方式,无法存档搜索结果。他们都转过身来,认为有一个用户已通过身份验证。没有它,所涉及的组件就没有真正意义。
经过数小时的反复试验,我找到了替代解决方案。我为这个用例添加了一个单独的环境
debugging
。它在 debug
中将 AppKernel.php
标志设置为 true。配置和安全组件看起来与 dev
环境相似,但有以下变化:
``
config_debugging.yml
imports:
- { resource: config.yml }
- { resource: security_debugging.yml }
- ...
services:
my.debugging.exception_listener:
class: My\Bundle\AppBundle\EventListener\DebuggingExceptionListener
tags:
- { name: kernel.event_listener, event: kernel.exception }
my.debugging.access_listener:
class: My\Bundle\AppBundle\EventListener\DebuggingAccessListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
arguments: [ '@security.authorization_checker', '@security.token_storage' ]
security_debugging.yml
security:
firewalls:
app_resources:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
...
main:
pattern: ^/
http_basic:
provider: fos_userbundle
access_control:
- { path: ^/admin/, role: ROLE_ADMIN }
- { path: ^/, role: ROLE_DEBUGGING }
role_hierarchy:
ROLE_ADMIN: [ROLE_USER,ROLE_DEBUGGING,ROLE_SONATA_ADMIN]
ROLE_SUPER_ADMIN: ROLE_ADMIN
两个听众在那里检查角色是否正确
ROLE_DEBUGGING
。然后每个管理员总是能够登录,并且在调试特定用户时,用户必须为他设置特定的角色。
DebuggingExceptionListener.php
...
public function onKernelException(GetResponseForExceptionEvent $event)
{
// You get the exception object from the received event
$exception = $event->getException();
if ($exception instanceof AccessDeniedHttpException) {
$event->setResponse(new RedirectResponse('/'));
}
}
...
DebuggingAccessListener.php
...
public function onKernelRequest(GetResponseEvent $event)
{
if (!$event->isMasterRequest()) {
return;
}
if ($this->tokenStorage->getToken() === null) {
return;
}
if ($this->authorizationChecker->isGranted('ROLE_DEBUGGING')) {
return;
}
$event->setResponse(new RedirectResponse('/'));
}
...
这样就可以检查几乎仍然受登录保护的漏洞应用程序。不可能调试所有涉及常用登录组件或任何 AccessDeniedHttpExceptions 的组件。但就目前而言,这是我能想到的最佳解决方案。我仍然希望有更好的解决方案。