Skip to main content

id: SECURITY title: SECURITY

Security

Secure Configuration

Global Security Parameters

Global Salt Configuration (common/config/params.php)

'globalSalt' => 'd9pAxeSPaFr2Xu3E',
// Admin Panel
'cookieValidationKey' => 'your-admin-cookie-validation-key',

// API
'cookieValidationKey' => 'your-api-cookie-validation-key',

// OIDC
'cookieValidationKey' => 'your-oidc-cookie-validation-key',

// Hotline
'cookieValidationKey' => 'your-hotline-cookie-validation-key',

CSRF Protection

Admin Panel CSRF

'request' => [
'csrfParam' => '_csrf-admin',
'baseUrl' => '/admin-panel',
],

API CSRF Configuration

'request' => [
'enableCookieValidation' => false,
'enableCsrfCookie' => false,
'baseUrl' => '',
'parsers' => [
'application/json' => 'yii\web\JsonParser',
],
],

OIDC CSRF Configuration

'request' => [
'csrfParam' => '_csrf-oidc',
'baseUrl' => '/oidc',
],

Session Security

Admin Panel Session

'user' => [
'identityClass' => User::class,
'authTimeout' => 60 * 60 * 12, // 12h
'absoluteAuthTimeout' => 60 * 60 * 12, // 12h
'identityCookie' => ['name' => '_identity-admin', 'httpOnly' => true],
],
'session' => [
'name' => 'advanced-admin',
'cookieParams' => ['lifetime' => 60 * 60 * 12],
'timeout' => 60 * 60 * 12
],

API Session

'user' => [
'identityClass' => 'api\models\UserWebStoreClientAccount',
'enableAutoLogin' => false,
'enableSession' => false,
],
'session' => [
'name' => 'advanced-api',
],

OIDC Session

'user' => [
'class' => UserComponent::class,
'identityClass' => User::class,
'enableAutoLogin' => false,
'authTimeout' => 60 * 60 * 6, // 6h
'identityCookie' => ['name' => '_identity-oidc', 'httpOnly' => true],
],
'session' => [
'name' => 'advanced-oidc',
],

JWT Token Security

Authorization Token Manager (api/components/AuthorizationTokenManager.php)

class AuthorizationTokenManager
{
private string $apiSalt = 'private_string';
public $algorithm = 'HS512';

public function createAuthToken(User $user, ClientAccount $clientAccount, WebStore $webStore = null): string
{
$tokenData = [
'iss' => $user->id, // Issuer
'client' => $clientAccount->id,
'store' => $webStore->id ?? null,
'aud' => Yii::$app->request->getUserHost(), // Audience
'iat' => time(), // Issued At
'nbf' => time(), // Not Before
];

return JWT::encode($tokenData, $this->apiSalt, $this->algorithm);
}
}

STACC Token Security (api/modules/stacc/components/StaccAuthorizationTokenManager.php)

class StaccAuthorizationTokenManager
{
private string $apiSalt = 'private_string';
public $algorithm = 'HS512';

public function createAuthToken(StaccUser $user)
{
$tokenData = [
'iss' => $user->id, // Issuer
'ip' => $user->ip,
'aud' => Yii::$app->request->getUserHost(), // Audience
'iat' => time(), // Issued At
'nbf' => time(), // Not Before
];

return JWT::encode($tokenData, $this->apiSalt, $this->algorithm);
}
}

OAuth2 Security

OIDC Module Configuration (oidc/config/oidc-module.php)

return [
'class' => Oauth2Module::class,
'identityClass' => User::class,
'privateKey' => 'file://' . Yii::getAlias('@keys/openid-connect-private.key'),
'publicKey' => 'file://' . Yii::getAlias('@keys/openid-connect-public.key'),
'privateKeyPassphrase' => getenv('YII2_OAUTH2_SERVER_PRIVATE_KEY_PASSPHRASE'),
'codesEncryptionKey' => getenv('YII2_OAUTH2_SERVER_CODES_ENCRYPTION_KEY'),
'storageEncryptionKeys' => [
'2024-01-01' => getenv('YII2_OAUTH2_SERVER_STORAGE_ENCRYPTION_KEY'),
],
'defaultStorageEncryptionKey' => '2024-01-01',
'defaultAccessTokenTTL' => 'P1D',
'grantTypes' => [
Oauth2BaseModule::GRANT_TYPE_AUTH_CODE,
Oauth2BaseModule::GRANT_TYPE_REFRESH_TOKEN,
],
'enableOpenIdConnect' => true,
'openIdConnectRpInitiatedLogoutEndpoint' => true,
];

Basic Authentication

STACC Basic Auth (api/modules/stacc/controllers/BaseController.php)

public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['basicAuth'] = [
'class' => HttpBasicAuth::class,
'auth' => function ($username, $password) {
$user = StaccUser::find()->where(['user_name' => $username, 'is_active' => true])->one();
if ($user && $user->validatePassword($password) && $user->validateIp(Yii::$app->request->getUserIP())) {
return $user;
}
return null;
},
];

return $behaviors;
}

IP Access Control

Swagger API Access Control (api/modules/stacc/controllers/SwaggerController.php)

public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'only' => ['api-docs', 'api-json'],
'rules' => [
[
'ips' => [
'127.0.0.1',
'::1',
'37.120.141.158',
// STACC IP addresses
'193.40.10.242',
'52.51.195.68',
// Security audit company
'88.196.134.147',
],
'allow' => true,
],
],
'denyCallback' => function () {
throw new ForbiddenHttpException(Yii::t('yii', 'You are not allowed to perform this action.'));
}
]
];
}

User Data Protection

Password Security

Password Hashing Algorithm

protected function getPasswordHash(string $password)
{
return hash('sha512', sprintf('%s-%s-%s',
Yii::$app->params['globalSalt'],
$password,
null
));
}

Password Validation

public function validatePassword(string $password)
{
return $this->getPasswordHash($password) === $this->password_hash;
}

Password Setting

public function setPassword(string $password)
{
$this->password_hash = $this->getPasswordHash($password);
}

Personal Data Protection

Personal ID Code Handling

// User model with personal ID code
'personal_id_code' => $userData['personal_id_code'],

GDPR Data Deletion

// Admin panel data deletion endpoint
'POST /user/delete-data/{userId}' - Delete user data (GDPR compliance)

Privacy Page Management

// Privacy page content management
ContentPage::PRIVACY_PAGE = 'PRIVACY';

public static function getPrivacyPages()
{
return self::find()->where(['content_page_type' => self::PRIVACY_PAGE]);
}

Data Encryption

User Pass Phrase Encryption

'userPassPhraseEncryptionPublicKeyPath' => Yii::getAlias("@common/config/keys/public.pem"),

LCP (License Content Protection)

'lcp' => [
'X509PemCertPath' => '/path/to/cert.pem',
'pemPrivateKeyPath' => '/path/to/privkey.pem',
'userPassPhraseEncryptionPublicKeyPath' => '/path/to/public.key'
],

Payment Security

'everypay' => [
'username' => 'your-everypay-username',
'secret' => 'your-everypay-secret',
'accountName' => 'your-account-name',
'testMode' => true,
'merchantIP' => '217.146.69.5',
],

File Storage Security

AWS S3 Bucket Security

'awsS3' => [
'publicBucket' => 'uus-rahvaraamat-staging-public',
'privateBucket' => 'uus-rahvaraamat-staging-private',
'bucketMap' => [
// Private buckets use "-private" postfix
\common\models\Product::class.'_private' => 'rahvaraamat-staging-product-private',
\common\models\OrderProduct::class.'_private' => 'rahvaraamat-staging-order-product-private',
\common\models\ProductAudiobookChapter::class.'_private' => 'audio-books-staging-private',
]
],

Protected Files Directory

// Name for protected file directory
defined('PROTECTED_FILES_DIRNAME') || define('PROTECTED_FILES_DIRNAME', 'files');

Data Anonymization

User Data Obfuscation

// API obfuscation flag
defined('API_OBFUSCATE_USER_DATA') || define('API_OBFUSCATE_USER_DATA', false);

Facebook Conversion Tracking

// Don't pass personal user data for guest accounts
// don't pass personal user data for guest accounts

Common Security Practices

Input Validation

Model Validation Rules

public function rules()
{
return [
[['log_time', 'category', 'level', 'message'], 'required'],
[['log_time'], 'safe'],
[['user_id'], 'integer'],
[['message', 'prefix'], 'string'],
[['category', 'level'], 'string', 'max' => 128],
[['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::class, 'targetAttribute' => ['user_id' => 'id']],
];
}

API Input Validation

// Form validation with error codes
'code' => 'VALIDATION_ERROR',
'message' => 'Validation failed',

SQL Injection Prevention

ActiveRecord Usage

// Use parameterized queries
$products = Product::find()
->where(['category_id' => $categoryId])
->andWhere(['like', 'name', $searchTerm])
->all();

Raw Query Security

// Use prepared statements for raw queries
$products = Yii::$app->db->createCommand(
'SELECT * FROM product WHERE category_id = :category_id AND price > :min_price'
)
->bindParam(':category_id', $categoryId)
->bindParam(':min_price', $minPrice)
->queryAll();

XSS Prevention

Output Encoding

// Escape output in views
<?= Html::encode($product->name) ?>

// Use ActiveForm for automatic escaping
<?= $form->field($model, 'name')->textInput() ?>

// Sanitize user input
$cleanDescription = Html::purify($userInput);

Access Control

Role-Based Access Control (RBAC)

// Define roles and permissions
public function behaviors(): array
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'actions' => ['index', 'view'],
'roles' => ['?', '@'],
],
[
'allow' => true,
'actions' => ['create', 'update', 'delete'],
'roles' => ['admin'],
],
],
],
];
}

API Authentication

// JWT Token validation
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::class,
];
return $behaviors;
}

Error Handling

Secure Error Messages

// Don't expose sensitive information in error messages
'errorHandler' => [
'errorAction' => 'site/error',
],

Logging Security

// Log security events
'log' => [
'targets' => [
[
'class' => 'common\components\CustomLogDbTarget',
'levels' => ['error', 'warning', 'info'],
'except' => [
'yii\db\*',
'yii\filters\*',
'yii\web\HttpException:404',
'yii\web\Session::open',
'yii\i18n\*',
'yii\web\User*',
'yii\debug\Module::checkAccess',
'yii\httpclient\*'
],
],
],
],

Environment Security

Development vs Production

// Disable debug in production
defined('YII_DEBUG') || define('YII_DEBUG', true);

// SSL configuration
defined('SSL_DISABLE') || define('SSL_DISABLE', false);

Database Security

// SQL Server segfault prevention
defined('API_SQL_SERVER_SEGFAULT_TRUNCATE') || define('API_SQL_SERVER_SEGFAULT_TRUNCATE', false);

CORS Configuration

OIDC CORS Settings

$behaviors['corsFilter'] = [
'class' => Cors::class,
'cors' => [
'Origin' => Yii::$app->params['corsOrigins'] ?? ['*'],
'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
'Access-Control-Request-Headers' => ['*'],
'Access-Control-Allow-Credentials' => true,
'Access-Control-Max-Age' => 3600,
]
];

Session Security

Redis Session Storage

'session' => [
'class' => 'yii\redis\Session',
'redis' => [
'hostname' => 'redis',
'port' => 6379,
'database' => 2,
],
'cookieParams' => [
'domain' => '.rahvaraamat.ee',
'httpOnly' => false,
'secure' => false,
'path' => '/',
'sameSite' => 'None',
],
],

Security Headers

Content Security Policy

// Register CSRF meta tags
<?php $this->registerCsrfMetaTags() ?>

Audit Trail

User Action Logging

// Log user actions with timestamps
'log_time' => date("Y-m-d H:i:s", (int)$timestamp),
'user_id' => Yii::$app->user->getId(),
'prefix' => $this->getMessagePrefix($message),

Login Tracking

public function afterLogin()
{
$this->last_login_date = date('Y-m-d H:i:s');
$this->saveSafe(true, ['last_login_date']);
}