错误:不允许您请求的操作,当CodeIgniter 4中的HTTP请求方法为“POST”时,筛选器阻止了我的API

n7taea2i  于 2022-12-07  发布在  其他
关注(0)|答案(2)|浏览(121)

我正在使用CI 4构建一个Web应用程序沿着Android API。
对于Web应用程序,我有一个过滤器来检查用户是否已经登录,但有一些例外,其中之一是如果URL包含api/*(API的URL是http://localip/api/),则忽略过滤器
如果请求方法是GET,API工作正常。我可以从API获取数据。但当我尝试使用POST方法将数据插入数据库时,它会将我重定向到登录页面(我正在使用Postman测试API)
我该如何解决这个问题?
到目前为止,我所尝试的是将登录筛选器别名添加到

public $methods = [
        'post' => ['csrf', 'loginfilter']
    ]; But still not working

下面是完整的代码

过滤器.php

<?php

namespace Config;

use App\Filters\CorsFilter;
use App\Filters\LoginFilter;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Filters\CSRF;
use CodeIgniter\Filters\DebugToolbar;
use CodeIgniter\Filters\Honeypot;
use CodeIgniter\Filters\InvalidChars;
use CodeIgniter\Filters\SecureHeaders;

class Filters extends BaseConfig
{
    /**
     * Configures aliases for Filter classes to
     * make reading things nicer and simpler.
     *
     * @var array
     */
    public $aliases = [
        'loginfilter' => LoginFilter::class,
        'cors' => CorsFilter::class
    ];

    /**
     * List of filter aliases that are always
     * applied before and after every request.
     *
     * @var array
     */
    public $globals = [
        'before' => [
            // 'honeypot',
            'csrf',
            'loginfilter' => ['except' => ['/', '/login', 'api/*']],
            'cors'
            // 'invalidchars',
        ],
        'after' => [
            'toolbar',
            // 'honeypot',
            // 'secureheaders',
        ],
    ];

    /**
     * List of filter aliases that works on a
     * particular HTTP method (GET, POST, etc.).
     *
     * Example:
     * 'post' => ['csrf', 'throttle']
     *
     * @var array
     */
    public $methods = [
        'post' => ['csrf','loginfilter]
    ];

    /**
     * List of filter aliases that should run on any
     * before or after URI patterns.
     *
     * Example:
     * 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']]
     *
     * @var array
     */
    public $filters = [];
}

登录过滤器.php

<?php

namespace App\Filters;

use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;

class LoginFilter implements FilterInterface
{

    public function before(RequestInterface $request, $arguments = null)
    {
        $session = session();
        if (!$session->has('user_id')) {
            return redirect()->to(base_url() . '/');
        }
    }

    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
    {
    }
}

路由.php

$routes->resource("api/role", ['controller' => 'apis\MasterDataRoleApi']);

主数据角色Api.php(控制器)

<?php

namespace App\Controllers\apis;

use App\Models\GeneralModels;
use App\Models\RoleModel;
use CodeIgniter\API\ResponseTrait;
use CodeIgniter\RESTful\ResourceController;

class MasterDataRoleApi extends ResourceController
{
    use ResponseTrait;
    protected $model;
    protected $generalModel;

    public function __construct()
    {
        $this->model = new RoleModel();
        $this->generalModel = new GeneralModels();
    }

    public function index()
    {
        $role= $this->request->getVar('role');
        $data = $this->model->getRoleApi($role);
        return $this->respond($data, 200);
    }

    public function create()
    {
        $roleName = $this->request->getPost('role_name');
        $supervisor = $this->request->getPost('supervisor');
        $userId = $this->request->getVar("userId");
        helper('idgenerator');
        $maxCode = $this->generalModel->getMaxData('tmrole', 'role_id');
        $generatedId = idGenerator($maxCode[0]['role_id'], 4, 3, "JAB-");

        $this->model->insertTmRole($generatedId, $roleName, $userId, $userId);
       

        $data = array();

        $dataArr = array(
            "response" => "Success",
            "response_details" => "Saved Successfully"
        );

        $data[] =  $dataArr;

        return $this->respondCreated($data, 201);
    }
}

下图显示了请求方法为GET https://i.stack.imgur.com/2ZKPd.png时返回的Json
下图显示了请求方法为POST https://i.stack.imgur.com/11R0z.png时返回的登录页面
先谢谢你了。

sy5wg1nm

sy5wg1nm1#

A部分:CSRF公司

说明1:

下图显示了请求方法为GET https://i.stack.imgur.com/2ZKPd.png时返回的Json
您的API GET请求工作正常,因为它们受保护。
跨站点请求伪造(CSRF)
CSRF保护仅适用于POST/PUT/PATCH/DELETE请求。其他方法的请求不受保护。

说明2:

下图显示了请求方法为POST https://i.stack.imgur.com/11R0z.png时返回的登录页面

错误

The action you requested is not allowed.

该错误来自system/Security/安全性.php::verify()
throw SecurityException::forDisallowedAction();
您通常会在两种情况下收到此错误:
1.当您忘记将CSRF令牌与请求沿着提交时(POST/PUT/etc.)。
1.当与HTML表单/请求正文沿着提交的CSRF令牌与CSRF Cookie中存在的令牌不匹配时。
打开CSRF筛选器后,当您发出HTTP请求但缺少CSRF Cookie时,系统会自动生成一个,并在HTTP Set-Cookie: '...'响应标头中发送。
如果您使用的是默认基于Cookie的CSRF保护,则在发出任何进一步的(POST/PUT/etc.)请求时,您需要在提交请求时提交匹配的CSRF令牌。
双重提交Cookie
这种技术很容易实现,并且是无状态的。在这种技术中,我们在cookie中发送一个随机值,并将其作为请求参数,服务器验证cookie值和请求值是否匹配。当用户访问(甚至在认证之前阻止CSRF登录),该站点应生成(密码编译强度)伪随机值,并将其设定为使用者计算机上的Cookie,与工作阶段识别项分开。然后网站要求每个交易请求都包含这个伪随机值作为一个隐藏的表单值(或其他请求参数/报头)。如果两者在服务器端匹配,服务器将接受它作为合法请求,如果不匹配,则拒绝该请求。

CSRF解决方案:

要通过 * 双重提交Cookie* 测试,每次发出(POST/PUT/etc.)请求时,您都需要提交一个CSRF Cookie和一个具有匹配CSRF标记的请求参数/标头。即:

需求1:Cookie: csrf_cookie_name=ccd8facfa8229bdba5e0160c108d1a02; HTTP要求信头。
要求2:

用户发送令牌的顺序
检查CSRF令牌可用性的顺序如下:

  1. $_POST阵列(即:<input type="hidden" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>" />)的数据。
  2. HTTP标头(即:X-CSRF-TOKEN: ccd8facfa8229bdba5e0160c108d1a02)的数据。
  3. php://input(JSON请求)-请记住,这种方法是最慢的一种,因为我们必须解码JSON,然后重新编码它。(即:(一个月12个月1x个月)
  • 要求1* 通常由浏览器自动完成(如果您从浏览器发起请求)。不过,您也可以手动完成。例如:在PHP主视图模板中,可以使用 AJAX :
<script>
        $.ajaxSetup({
            headers: { 
                "<?php echo csrf_header();?>": "<?php echo csrf_hash();?>",

                "Cookie": "<?php echo csrf_token();?>"="<?php echo csrf_hash();?>"; "<?php echo session_name();?>":"<?php echo session_id();?>"
             }
        });

<script>

下面是我个人的.env文件配置,对我来说很有效。
myapp.local 更改为应用程序的域。
.env档案。

app.baseURL = 'http://myapp.local'
app.sessionExpiration = 86400

cookie.domain = '.myapp.local'
cookie.httponly = false

security.expires = 86400
security.regenerate = false

与 * 安全性/CSRF、COOKIE、APP/会话 * 相关的其余.env文件配置保持其默认设置。
在CSRF部分的最后,如果你仍然有问题,安装和配置Xdebug,在这个方法**system/Security/Security.php::verify()**中添加断点,并确认你的HTTP请求是否通过了那里的条件。确保你的Xdebug配置类似于:(xdebug.log_level = 0 | xdebug.mode = debug,develop | xdebug.start_with_request = yes | xdebug.client_port = 9004 | xdebug.client_host = "localhost" | xdebug.trace_output_name = "trace.%c.%t-%s.%H_%R" | xdebug.profiler_output_name = "cachegrind.out.%t-%s.%H_%R" | xdebug.remote_handler = "dbgp" | xdebug.show_local_vars = 9

B部分:或Insomnia

本节介绍如何设置REST API客户端应用程序工具,以便能够测试项目的端点。

HTTP请求标头要求:

  1. X-Requested-With: XMLHttpRequest
  2. X-CSRF-TOKEN: 62b04a891414ef789bee7108f94ad97a
  3. Content-Type: application/x-www-form-urlencoded
  4. x1米20英寸
  5. Cookie: csrf_cookie_name=62b04a891414ef789bee7108f94ad97a; ci_session=4ji7amn186ckbo0gutdoe3ai6ufumk4e
  6. User-Agent: insomnia/2022.1.1
  7. Host: myapp.local
  • 以上标头是必需的。您可以在上面列出的标头之后添加其他HTTP请求标头。*

User-Agent可以根据您使用的是一种工具还是另一种工具而变化(即失眠或 Postman )。
Host也会有所不同,具体取决于您如何设置应用程序的基本URL。
Content-Type可以根据您提交给服务器的数据类型而变化。(例如HTML FORM -〉application/x-www-form-urlencoded和JSON -〉application/json)。
对于Cookie头,如果您的端点位于身份验证系统或登录过滤器之后,您可能需要附加ci_session(session_id)cookie。当然,如果您的API使用不同的身份验证机制(即:此外,如果您的应用程序已启用csrf筛选器,则可能需要附加如上所示的csrf_cookie_name Cookie。在这种情况下,您可能希望创建一个专用的GET API路由端点,专门用于接收csrf Cookie名称和值(哈希)。或者,如果你懒得设置一个专门的端点来发送CSRF令牌,并且你已经运行了Web应用程序,在登录/浏览你的应用程序时打开浏览器的控制台,在应用程序选项卡-〉Cookie-〉你的域中,你应该能够在Postman / Insomnia中看到并使用这些Cookie。

X-CSRF-TOKEN HTTP请求标头值应与csrf_cookie_name Cookie值相同

HTTP请求调用要求:

即:
x1米38英寸

  • 请求正文。不要忘记将csrf_cookie_name标记添加为请求正文的一部分。*

| 关键字|数值|
| - -|- -|
| 公司名称|特斯拉公司|
| csrf_cookie_名称|一种用于汽车的制动器|

我以 * 第B部分结束:*

| 重要事项|项目名称|
| - -|- -|
| ci_session Cookie***或***Authorization: Bearer xxxxxxx HTTP请求标头。|允许您***仅***对受身份验证保护的API终结点使用应用程序/项目进行身份验证。在您的特定情况下,我相信您的loginfilter正在使用ci_session Cookie,并且该Cookie应该在Cookie HTTP请求标头的帮助下随每个请求沿着发送。|
| csrf_cookie_name Cookie***和***(X-CSRF-TOKEN HTTP请求标头***或***CSRF令牌请求参数)。|CSRF Cookie***和***(X-CSRF-TOKEN HTTP请求标头*或*CSRF标记请求参数)值必须匹配。如果已打开csrf过滤器,则这是必需的。|

pgx2nnw8

pgx2nnw82#

从我所看到的是,你有一个loginfilter作为每个POST方法的后备。这可能是罪魁祸首。
也就是说,这里有一个替代的解决方案。您可以在routes.php中对路由进行分组,并对这些路由应用loginfilter。此外,您还可以根据需要对它们进行嵌套和分区。
示例:

$routes->group(
    'api',
    ['filter' => 'loginfilter'],
    function ($routes) {
        $routes->resource("role", ['controller' => 'apis\MasterDataRoleApi']);
    }
);

您可以在使用此方法时删除全局筛选器。

相关问题