Skip to content

Commit b1d7b76

Browse files
authored
Merge pull request #3 from Innovix-Matrix-Systems/IMS-4
IMS-4: XSECURE MODE Activated!!
2 parents 68d6db2 + 7ef3081 commit b1d7b76

File tree

10 files changed

+346
-50
lines changed

10 files changed

+346
-50
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ APP_URL=http://localhost
66

77
TELESCOPE_ENABLED=true
88

9+
#IMS XSECURE CONFIG
10+
XSECURITY_ENABLED=true
11+
XSECURITY_SECRET=
12+
913
LOG_CHANNEL=stack
1014
LOG_DEPRECATIONS_CHANNEL=null
1115
LOG_LEVEL=debug

.env.testing.example

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ APP_DEBUG=true
55
APP_URL=http://localhost
66

77
TELESCOPE_ENABLED=true
8-
DEBUGBAR_ENABLED=true
8+
9+
#IMS XSECURE CONFIG
10+
XSECURITY_ENABLED=false
11+
XSECURITY_SECRET=
912

1013
LOG_CHANNEL=stack
1114
LOG_DEPRECATIONS_CHANNEL=null

README.md

Lines changed: 130 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ Explore this project and experience the convenience of a ready-made local develo
1010

1111
## Features
1212

13-
- **Authentication using Laravel Sanctum**: Implement secure authentication using [Laravel Sanctum](https://laravel.com/docs/11.x/sanctum).
13+
- **Authentication using Laravel Sanctum**: Implement secure authentication using [Laravel Sanctum](https://laravel.com/docs/11.x/sanctum).
1414

15-
- **Role & Permission-Based Authorization**: Utilize [Laravel Permission](https://spatie.be/docs/laravel-permission/v6/introduction) for a flexible authorization system based on roles and permissions.
16-
17-
- **Multiple Language Support**: Provide a multilingual experience with [Laravel Lang](https://laravel-lang.com/) to make your application accessible to a diverse user base.
15+
- **Role & Permission-Based Authorization**: Utilize [Laravel Permission](https://spatie.be/docs/laravel-permission/v6/introduction) for a flexible authorization system based on roles and permissions.
1816

17+
- **Multiple Language Support**: Provide a multilingual experience with [Laravel Lang](https://laravel-lang.com/) to make your application accessible to a diverse user base.
1918

2019
## Required Commands to run locally
2120

@@ -95,7 +94,6 @@ php artisan ide-helper:models -N
9594
php artisan ide-helper:models --nowrite
9695
```
9796

98-
9997
9.**Fix PHP Lint and Run CS Fixer:**
10098
To fix PHP lint issues and run the Code Style Fixer, use the following command:
10199

@@ -111,7 +109,9 @@ Open your web browser or use a tool like `curl` to access the health check endpo
111109
```bash
112110
http://127.0.0.1:8000/api/healthz
113111
```
112+
114113
Upon hitting the health check endpoint, the app should respond with a JSON object similar to the following:
114+
115115
```json
116116
{
117117
"cache": true,
@@ -128,8 +128,125 @@ Verifying the health of your application is an essential step to ensure that all
128128

129129
Remember to perform this health check regularly, especially after making significant changes to your application or its environment.
130130

131+
## XSECURE Setup
132+
133+
IMS introduces an additional layer of security, enhancing the API's reliability and resilience. With this system, only applications possessing a shared `XSECURITY_TOKEN` can send API requests to the server; others will be blocked. To get started, follow the guide below.
134+
135+
### Getting Started
136+
137+
By default, XSecure is disabled! To enable it, set the `XSECURITY_ENABLED` value to true in your `.env` file:
138+
139+
```bash
140+
XSECURITY_ENABLED=true
141+
```
142+
143+
Other wise it will be disabled.
144+
145+
### Installation
146+
147+
Execute the following command to set up XSECURE:
148+
149+
```bash
150+
php artisan xsecure:install
151+
```
152+
153+
This command generates a secret for your application and updates your .env file with the `XSECURITY_SECRET` field.
154+
155+
After running the command, you will receive output similar to this:
156+
157+
```bash
158+
Generated secret: zSaFVDakUcuT4+FVSom6RZTnCu7o/15PbtEBLwLNZxY=
159+
XSECURITY_SECRET key have been updated in the .env file.
160+
```
161+
162+
Use this secret in your frontend or mobile app to generate a short-lived XSecure token, which will be verified by the backend server.
163+
164+
### Token Generation
165+
166+
Use the following examples to generate an `XSecure` token:
167+
168+
### Typescript/Javascript Example
169+
170+
```ts
171+
import crypto from "crypto";
172+
173+
export const generateXsecureToken = (secretKey: string): string => {
174+
// Calculate expiry timestamp (1 minute from current time)
175+
const expiryTimestamp = Math.floor(Date.now() / 1000) + 60;
176+
177+
// Create payload object with expiry timestamp
178+
const payload = { expiry: expiryTimestamp };
179+
180+
// Encode payload to Base64
181+
const token = Buffer.from(JSON.stringify(payload)).toString("base64");
182+
183+
// Create HMAC signature using SHA-256 hash function
184+
const hmac = crypto.createHmac("sha256", secretKey);
185+
186+
hmac.update(token);
187+
const signature = hmac.digest("hex");
188+
189+
// Combine token and signature separated by a period
190+
return `${token}.${signature}`;
191+
};
192+
```
193+
194+
### Flutter/Dart Example
195+
196+
```dart
197+
import 'dart:convert';
198+
import 'package:crypto/crypto.dart';
199+
200+
String generateXsecureToken(String secretKey) {
201+
// Calculate expiry timestamp (1 minute from current time)
202+
int expiryTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000 + 60;
203+
204+
// Create payload map with expiry timestamp
205+
Map<String, dynamic> payload = {'expiry': expiryTimestamp};
206+
207+
// Encode payload to Base64
208+
String token = base64Url.encode(utf8.encode(jsonEncode(payload)));
209+
210+
// Create HMAC signature using SHA-256 hash function
211+
Hmac hmac = Hmac(sha256, utf8.encode(secretKey));
212+
String signature = hmac.convert(utf8.encode(token)).toString();
213+
214+
// Combine token and signature separated by a period
215+
return '$token.$signature';
216+
}
217+
218+
```
219+
220+
### Example Token
221+
222+
The generated token will resemble the following:
223+
224+
```bash
225+
eyJleHBpcnkiOjE3MTE5MDE2NjJ9.89b9c45cffee0072ea160441e2462a7ae2de8b484f5d1f5bf4e57f90b1340e0c
226+
```
227+
228+
Ensure you include this token in the `X-SECURITY-TOKEN` header when sending requests to the backend server for verification:
229+
230+
```bash
231+
X-SECURITY-TOKEN: eyJleHBpcnkiOjE3MTE5MDE2NjJ9.89b9c45cffee0072ea160441e2462a7ae2de8b484f5d1f5bf4e57f90b1340e0c
232+
```
233+
234+
This header will authenticate and authorize your requests, ensuring secure communication with the backend server.
235+
236+
### Handling Invalid Tokens
237+
238+
If you send an invalid token, you will receive a JSON response like this:
239+
240+
```bash
241+
{
242+
"error": "Invalid XSECURE token"
243+
}
244+
```
245+
131246
## Running Test
247+
132248
To execute tests for your application, utilize the following command:
249+
133250
```bash
134251
./vendor/bin/pest
135252
```
@@ -141,23 +258,31 @@ php artisan test
141258
Running tests is crucial to ensure the reliability and correctness of your application's functionality. The above command will initiate the testing process and provide you with valuable insights into the quality of your codebase.
142259

143260
## Extra Artisan Commands
261+
144262
This project provides additional Artisan commands to simplify your workflow and enhance productivity.
145263

146264
### Run PHP CS Fixer
265+
147266
```bash
148267
php artisan csfixer:run
149268
```
269+
150270
This command ensures that your code adheres to the predefined coding standards, making your codebase clean and readable.
271+
151272
### Create a Service
273+
152274
Creating services for your application is made effortless. Use the following command to generate a service:
275+
153276
```bash
154277
php artisan make:service subfolder/ServiceName
155278
```
279+
156280
Replace subfolder and ServiceName with the actual values you need. You can also create a service without a subfolder:
157281

158282
```bash
159283
php artisan make:service TestService
160284
```
285+
161286
The newly created service will be located at `app/Http/Services/TestService.php`, ready to handle your application's business logic.
162287

163288
Leverage these Artisan commands to streamline your development process and maintain a well-structured codebase.
@@ -171,4 +296,3 @@ Leverage these Artisan commands to streamline your development process and maint
171296
This project is brought to you by Innovix Matrix System and is released as open-source software under the [MIT license](https://opensource.org/licenses/MIT).
172297

173298
Feel free to use, modify, and distribute this starter project in accordance with the MIT license terms. We encourage collaboration and welcome contributions from the community to make this project even better.
174-
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use Illuminate\Console\Command;
6+
7+
class XSecureSetup extends Command
8+
{
9+
/**
10+
* The name and signature of the console command.
11+
*
12+
* @var string
13+
*/
14+
protected $signature = 'xsecure:install';
15+
16+
/**
17+
* The console command description.
18+
*
19+
* @var string
20+
*/
21+
protected $description = 'Generate a secure secret for XSecure and update .env file';
22+
23+
/**
24+
* Execute the console command.
25+
*
26+
* @return int
27+
*/
28+
public function handle()
29+
{
30+
// Check if XSECURITY_ENABLED is true
31+
if (config('app.xsecure_enabled') !== true) {
32+
$this->error('XSECURITY_ENABLED is not set to true. Secret and token cannot be generated.');
33+
exit(1);
34+
}
35+
36+
// Generate the secret and token using openssl command
37+
$secret = trim(shell_exec('openssl rand -base64 32'));
38+
39+
if (empty($secret)) {
40+
$this->error('Failed to generate secret or token using openssl command.');
41+
exit(1);
42+
}
43+
44+
// Read the existing .env file
45+
$envFilePath = base_path('.env');
46+
$envContent = file_get_contents($envFilePath);
47+
48+
// Replace the existing XSECURITY_SECRET value
49+
$envContent = preg_replace('/^XSECURITY_SECRET=(.*)/m', 'XSECURITY_SECRET=' . $secret, $envContent);
50+
51+
// Update the .env file with the new values
52+
if (file_put_contents($envFilePath, $envContent) !== false) {
53+
// Print the generated secret and token on the screen
54+
$this->info('Generated secret: ' . $secret);
55+
$this->info('XSECURITY_SECRET key have been updated in the .env file.');
56+
exit(0);
57+
} else {
58+
$this->error('Failed to update .env file.');
59+
exit(1);
60+
}
61+
}
62+
}

app/Http/Kernel.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class Kernel extends HttpKernel
2222
\App\Http\Middleware\TrimStrings::class,
2323
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
2424
\App\Http\Middleware\LanguageMiddleware::class,
25+
\App\Http\Middleware\XSecurityMiddleware::class,
2526
];
2627

2728
/**
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
namespace App\Http\Middleware;
4+
5+
use App\Traits\RateLimitterTrait;
6+
use Closure;
7+
use Illuminate\Http\Request;
8+
use Symfony\Component\HttpFoundation\Response;
9+
10+
class XSecurityMiddleware
11+
{
12+
use RateLimitterTrait;
13+
const MAX_ATTEMPTS = 5;
14+
const MAX_DECAY_MINUTES = 1;
15+
/**
16+
* Handle an incoming request.
17+
*
18+
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
19+
*/
20+
public function handle(Request $request, Closure $next): Response
21+
{
22+
if (!config('app.xsecure_enabled')) {
23+
return $next($request);
24+
}
25+
if ($this->limiter()->tooManyAttempts(
26+
$this->throttleKey($request),
27+
self::MAX_ATTEMPTS,
28+
)) {
29+
return response()->json(['error' => 'Too many requests. Please try again later.'], 429);
30+
}
31+
32+
// Check CSRF token validity
33+
if (!$this->isValidXSecureToken($request->header('X-SECURITY-TOKEN') ?? '')) {
34+
// Increment failed attempts for the client
35+
$this->limiter()->hit($this->throttleKey($request), self::MAX_DECAY_MINUTES * 60);
36+
37+
return response()->json(['error' => 'Invalid XSECURE token'], 403);
38+
}
39+
40+
// Reset failed attempts for the client
41+
$this->limiter()->clear($this->throttleKey($request));
42+
43+
return $next($request);
44+
}
45+
/**
46+
* Validate XSECURE token against the shared secret key.
47+
*
48+
* @param string $token
49+
* @return bool
50+
*/
51+
52+
private function isValidXSecureToken(string $signedToken): bool
53+
{
54+
$sharedSecretKey = config('app.xsecure_secret');
55+
$parts = explode('.', $signedToken);
56+
if (count($parts) !== 2) {
57+
// Invalid format: $signedToken does not contain a token and signature
58+
return false;
59+
}
60+
// Extract token and signature
61+
list($token, $signature) = explode('.', $signedToken);
62+
// Calculate expected signature
63+
$expectedSignature = hash_hmac('sha256', $token, $sharedSecretKey);
64+
// Verify signature
65+
if (!hash_equals($expectedSignature, $signature)) {
66+
return false; // Signature verification failed
67+
}
68+
// Parse token payload
69+
$payload = json_decode(base64_decode($token), true);
70+
// Validate token expiry
71+
if ($payload === null || !isset($payload['expiry'])) {
72+
return false; // Invalid token format or missing expiry
73+
}
74+
return time() < $payload['expiry']; // Check if token has expired
75+
}
76+
77+
}

0 commit comments

Comments
 (0)