Issues (26)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Controller/Cas20Controller.php (3 issues)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\casserver\Controller;
6
7
use SimpleSAML\CAS\Constants as C;
8
use SimpleSAML\Configuration;
9
use SimpleSAML\Logger;
10
use SimpleSAML\Module;
11
use SimpleSAML\Module\casserver\Cas\Factories\TicketFactory;
12
use SimpleSAML\Module\casserver\Cas\Protocol\Cas20;
13
use SimpleSAML\Module\casserver\Cas\Ticket\TicketStore;
14
use SimpleSAML\Module\casserver\Controller\Traits\TicketValidatorTrait;
15
use SimpleSAML\Module\casserver\Controller\Traits\UrlTrait;
16
use SimpleSAML\Module\casserver\Http\XmlResponse;
17
use SimpleSAML\Utils;
18
use Symfony\Component\HttpFoundation\Request;
19
use Symfony\Component\HttpFoundation\Response;
20
use Symfony\Component\HttpKernel\Attribute\AsController;
21
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
22
23
#[AsController]
24
class Cas20Controller
25
{
26
    use UrlTrait;
27
    use TicketValidatorTrait;
28
29
    /** @var Logger */
30
    protected Logger $logger;
31
32
    /** @var Utils\HTTP */
33
    protected Utils\HTTP $httpUtils;
34
35
    /** @var Configuration */
36
    protected Configuration $casConfig;
37
38
    /** @var Cas20 */
39
    protected Cas20 $cas20Protocol;
40
41
    /** @var TicketFactory */
42
    protected TicketFactory $ticketFactory;
43
44
    /** @var TicketStore */
45
    protected TicketStore $ticketStore;
46
47
    /**
48
     * @param   Configuration       $sspConfig
49
     * @param   Configuration|null  $casConfig
50
     * @param   TicketStore|null    $ticketStore
51
     * @param   Utils\HTTP|null           $httpUtils
52
     *
53
     * @throws \Exception
54
     */
55
    public function __construct(
56
        private readonly Configuration $sspConfig,
57
        Configuration $casConfig = null,
58
        TicketStore $ticketStore = null,
59
        Utils\HTTP $httpUtils = null,
60
    ) {
61
        // We are using this work around in order to bypass Symfony's autowiring for cas configuration. Since
62
        // the configuration class is the same, it loads the ssp configuration twice. Still, we need the constructor
63
        // argument in order to facilitate testing
64
        $this->casConfig = ($casConfig === null || $casConfig === $sspConfig)
65
            ? Configuration::getConfig('module_casserver.php') : $casConfig;
66
        $this->cas20Protocol = new Cas20($this->casConfig);
67
        /* Instantiate ticket factory */
68
        $this->ticketFactory = new TicketFactory($this->casConfig);
69
        /* Instantiate ticket store */
70
        $ticketStoreConfig = $this->casConfig->getOptionalValue(
71
            'ticketstore',
72
            ['class' => 'casserver:FileSystemTicketStore'],
73
        );
74
        $ticketStoreClass = Module::resolveClass($ticketStoreConfig['class'], 'Cas\Ticket');
75
        $this->ticketStore = $ticketStore ?? new $ticketStoreClass($this->casConfig);
76
        $this->httpUtils = $httpUtils ?? new Utils\HTTP();
77
    }
78
79
    /**
80
     * @param   Request      $request
81
     * @param   string|null  $TARGET   Query parameter name for "service" used by older CAS clients'
82
     * @param   bool         $renew    [OPTIONAL] - if this parameter is set, ticket validation will only succeed
83
     *                                 if the service ticket was issued from the presentation of the user’s primary
84
     *                                 credentials. It will fail if the ticket was issued from a single sign-on session.
85
     * @param   string|null  $ticket   [REQUIRED] - the service ticket issued by /login
86
     * @param   string|null  $service  [REQUIRED] - the identifier of the service for which the ticket was issued
87
     * @param   string|null  $pgtUrl   [OPTIONAL] - the URL of the proxy callback
88
     *
89
     * @return XmlResponse
90
     */
91
    public function serviceValidate(
92
        Request $request,
93
        #[MapQueryParameter] ?string $TARGET = null,
94
        #[MapQueryParameter] bool $renew = false,
95
        #[MapQueryParameter] ?string $ticket = null,
96
        #[MapQueryParameter] ?string $service = null,
97
        #[MapQueryParameter] ?string $pgtUrl = null,
98
    ): XmlResponse {
99
        return $this->validate(
100
            request: $request,
101
            method:  'serviceValidate',
102
            renew:   $renew,
103
            target:  $TARGET,
104
            ticket:  $ticket,
105
            service: $service,
106
            pgtUrl:  $pgtUrl,
107
        );
108
    }
109
110
    /**
111
     * /proxy provides proxy tickets to services that have
112
     * acquired proxy-granting tickets and will be proxying authentication to back-end services.
113
     *
114
     * @param   Request      $request
115
     * @param   string|null  $targetService  [REQUIRED] - the service identifier of the back-end service.
116
     * @param   string|null  $pgt            [REQUIRED] - the proxy-granting ticket acquired by the service
117
     *                                       during service ticket or proxy ticket validation.
118
     *
119
     * @return XmlResponse
120
     * @throws \ErrorException
121
     */
122
    public function proxy(
123
        Request $request,
124
        #[MapQueryParameter] ?string $targetService = null,
125
        #[MapQueryParameter] ?string $pgt = null,
126
    ): XmlResponse {
127
        // NOTE: Here we do not override the configuration
128
        $legal_target_service_urls = $this->casConfig->getOptionalValue('legal_target_service_urls', []);
129
        // Fail if
130
        $message = match (true) {
131
            // targetService parameter is not defined
132
            $targetService === null => 'Missing target service parameter [targetService]',
133
            // pgt parameter is not defined
134
            $pgt === null => 'Missing proxy granting ticket parameter: [pgt]',
135
            !$this->checkServiceURL($this->sanitize($targetService), $legal_target_service_urls) =>
0 ignored issues
show
It seems like $targetService can also be of type null; however, parameter $parameter of SimpleSAML\Module\casser...0Controller::sanitize() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

135
            !$this->checkServiceURL($this->sanitize(/** @scrutinizer ignore-type */ $targetService), $legal_target_service_urls) =>
Loading history...
136
                "Target service parameter not listed as a legal service: [targetService] = {$targetService}",
137
            default => null,
138
        };
139
140
        if (!empty($message)) {
141
            return new XmlResponse(
142
                (string)$this->cas20Protocol->getValidateFailureResponse(C::ERR_INVALID_REQUEST, $message),
143
                Response::HTTP_BAD_REQUEST,
144
            );
145
        }
146
147
        // Get the ticket
148
        $proxyGrantingTicket = $this->ticketStore->getTicket($pgt);
0 ignored issues
show
It seems like $pgt can also be of type null; however, parameter $ticketId of SimpleSAML\Module\casser...icketStore::getTicket() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

148
        $proxyGrantingTicket = $this->ticketStore->getTicket(/** @scrutinizer ignore-type */ $pgt);
Loading history...
149
        $message = match (true) {
150
            // targetService parameter is not defined
151
            $proxyGrantingTicket === null => "Ticket {$pgt} not recognized",
152
            // pgt parameter is not defined
153
            !$this->ticketFactory->isProxyGrantingTicket($proxyGrantingTicket)
0 ignored issues
show
It seems like $proxyGrantingTicket can also be of type null; however, parameter $ticket of SimpleSAML\Module\casser...isProxyGrantingTicket() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

153
            !$this->ticketFactory->isProxyGrantingTicket(/** @scrutinizer ignore-type */ $proxyGrantingTicket)
Loading history...
154
            => "Not a valid proxy granting ticket id: {$pgt}",
155
            default => null,
156
        };
157
158
        if (!empty($message)) {
159
            return new XmlResponse(
160
                (string)$this->cas20Protocol->getValidateFailureResponse('BAD_PGT', $message),
161
                Response::HTTP_BAD_REQUEST,
162
            );
163
        }
164
165
        // Get the session id from the ticket
166
        $sessionTicket = $this->ticketStore->getTicket($proxyGrantingTicket['sessionId']);
167
168
        if (
169
            $sessionTicket === null
170
            || $this->ticketFactory->isSessionTicket($sessionTicket) === false
171
            || $this->ticketFactory->isExpired($sessionTicket)
172
        ) {
173
            $message = "Ticket {$pgt} has expired";
174
            Logger::debug('casserver:' . $message);
175
176
            return new XmlResponse(
177
                (string)$this->cas20Protocol->getValidateFailureResponse('BAD_PGT', $message),
178
                Response::HTTP_BAD_REQUEST,
179
            );
180
        }
181
182
        $proxyTicket = $this->ticketFactory->createProxyTicket(
183
            [
184
                'service' => $targetService,
185
                'forceAuthn' => $proxyGrantingTicket['forceAuthn'],
186
                'attributes' => $proxyGrantingTicket['attributes'],
187
                'proxies' => $proxyGrantingTicket['proxies'],
188
                'sessionId' => $proxyGrantingTicket['sessionId'],
189
                ],
190
        );
191
192
        $this->ticketStore->addTicket($proxyTicket);
193
194
        return new XmlResponse(
195
            (string)$this->cas20Protocol->getProxySuccessResponse($proxyTicket['id']),
196
            Response::HTTP_OK,
197
        );
198
    }
199
200
    /**
201
     * @param   Request      $request
202
     * @param   string|null  $TARGET   Query parameter name for "service" used by older CAS clients'
203
     * @param   bool         $renew    [OPTIONAL] - if this parameter is set, ticket validation will only succeed
204
     *                                 if the service ticket was issued from the presentation of the user’s primary
205
     *                                 credentials. It will fail if the ticket was issued from a single sign-on session.
206
     * @param   string|null  $ticket   [REQUIRED] - the service ticket issued by /login
207
     * @param   string|null  $service  [REQUIRED] - the identifier of the service for which the ticket was issued
208
     * @param   string|null  $pgtUrl   [OPTIONAL] - the URL of the proxy callback
209
     *
210
     * @return XmlResponse
211
     */
212
    public function proxyValidate(
213
        Request $request,
214
        #[MapQueryParameter] ?string $TARGET = null,
215
        #[MapQueryParameter] bool $renew = false,
216
        #[MapQueryParameter] ?string $ticket = null,
217
        #[MapQueryParameter] ?string $service = null,
218
        #[MapQueryParameter] ?string $pgtUrl = null,
219
    ): XmlResponse {
220
        return $this->validate(
221
            request: $request,
222
            method:  'proxyValidate',
223
            renew:   $renew,
224
            target:  $TARGET,
225
            ticket:  $ticket,
226
            service: $service,
227
            pgtUrl:  $pgtUrl,
228
        );
229
    }
230
231
    /**
232
     * Used by the unit tests
233
     *
234
     * @return TicketStore
235
     */
236
    public function getTicketStore(): TicketStore
237
    {
238
        return $this->ticketStore;
239
    }
240
}
241