Issues (31)

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 \SimpleSAML\Logger */
30
    protected Logger $logger;
31
32
    /** @var \SimpleSAML\Utils\HTTP */
33
    protected Utils\HTTP $httpUtils;
34
35
    /** @var \SimpleSAML\Configuration */
36
    protected Configuration $casConfig;
37
38
    /** @var \SimpleSAML\Module\casserver\Cas\Protocol\Cas20 */
39
    protected Cas20 $cas20Protocol;
40
41
    /** @var \SimpleSAML\Module\casserver\Cas\Factories\TicketFactory */
42
    protected TicketFactory $ticketFactory;
43
44
    /** @var \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore */
45
    protected TicketStore $ticketStore;
46
47
    /**
48
     * @param \SimpleSAML\Configuration $sspConfig
49
     * @param \SimpleSAML\Configuration|null $casConfig
50
     * @param \SimpleSAML\Module\casserver\Cas\Ticket\TicketStore|null $ticketStore
51
     * @param \SimpleSAML\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
68
        /* Instantiate ticket factory */
69
        $this->ticketFactory = new TicketFactory($this->casConfig);
70
71
        /* Instantiate ticket store */
72
        $ticketStoreConfig = $this->casConfig->getOptionalValue(
73
            'ticketstore',
74
            ['class' => 'casserver:FileSystemTicketStore'],
75
        );
76
        $ticketStoreClass = Module::resolveClass($ticketStoreConfig['class'], 'Cas\Ticket');
77
        $this->ticketStore = $ticketStore ?? new $ticketStoreClass($this->casConfig);
78
        $this->httpUtils = $httpUtils ?? new Utils\HTTP();
79
    }
80
81
    /**
82
     * @param \Symfony\Component\HttpFoundation\Request $request
83
     * @param string|null $TARGET   Query parameter name for "service" used by older CAS clients'
84
     * @param bool $renew           [OPTIONAL] - if this parameter is set, ticket validation will only succeed
85
     *                                 if the service ticket was issued from the presentation of the user’s primary
86
     *                                 credentials. It will fail if the ticket was issued from a single sign-on session.
87
     * @param string|null $ticket   [REQUIRED] - the service ticket issued by /login
88
     * @param string|null $service  [REQUIRED] - the identifier of the service for which the ticket was issued
89
     * @param string|null $pgtUrl   [OPTIONAL] - the URL of the proxy callback
90
     *
91
     * @return \SimpleSAML\Module\casserver\Http\XmlResponse
92
     */
93
    public function serviceValidate(
94
        Request $request,
95
        #[MapQueryParameter] ?string $TARGET = null,
96
        #[MapQueryParameter] bool $renew = false,
97
        #[MapQueryParameter] ?string $ticket = null,
98
        #[MapQueryParameter] ?string $service = null,
99
        #[MapQueryParameter] ?string $pgtUrl = null,
100
    ): XmlResponse {
101
        return $this->validate(
102
            request: $request,
103
            method:  'serviceValidate',
104
            renew:   $renew,
105
            target:  $TARGET,
106
            ticket:  $ticket,
107
            service: $service,
108
            pgtUrl:  $pgtUrl,
109
        );
110
    }
111
112
    /**
113
     * /proxy provides proxy tickets to services that have
114
     * acquired proxy-granting tickets and will be proxying authentication to back-end services.
115
     *
116
     * @param \Symfony\Component\HttpFoundation\Request $request
117
     * @param string|null $targetService  [REQUIRED] - the service identifier of the back-end service.
118
     * @param string|null $pgt            [REQUIRED] - the proxy-granting ticket acquired by the service
119
     *                                       during service ticket or proxy ticket validation.
120
     *
121
     * @return \SimpleSAML\Module\casserver\Http\XmlResponse
122
     * @throws \ErrorException
123
     */
124
    public function proxy(
125
        Request $request,
126
        #[MapQueryParameter] ?string $targetService = null,
127
        #[MapQueryParameter] ?string $pgt = null,
128
    ): XmlResponse {
129
        // NOTE: Here we do not override the configuration
130
        $legal_target_service_urls = $this->casConfig->getOptionalValue('legal_target_service_urls', []);
131
        // Fail if
132
        $message = match (true) {
133
            // targetService parameter is not defined
134
            $targetService === null => 'Missing target service parameter [targetService]',
135
            // pgt parameter is not defined
136
            $pgt === null => 'Missing proxy granting ticket parameter: [pgt]',
137
            !$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

137
            !$this->checkServiceURL($this->sanitize(/** @scrutinizer ignore-type */ $targetService), $legal_target_service_urls) =>
Loading history...
138
                "Target service parameter not listed as a legal service: [targetService] = {$targetService}",
139
            default => null,
140
        };
141
142
        if (!empty($message)) {
143
            return new XmlResponse(
144
                (string)$this->cas20Protocol->getValidateFailureResponse(C::ERR_INVALID_REQUEST, $message),
145
                Response::HTTP_BAD_REQUEST,
146
            );
147
        }
148
149
        // Get the ticket
150
        $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

150
        $proxyGrantingTicket = $this->ticketStore->getTicket(/** @scrutinizer ignore-type */ $pgt);
Loading history...
151
        $message = match (true) {
152
            // targetService parameter is not defined
153
            $proxyGrantingTicket === null => "Ticket {$pgt} not recognized",
154
            // pgt parameter is not defined
155
            !$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

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