I'm developing a Social Engineering Awareness Training Application. This is the focus of my thesis for my undergraduate degree. This will be a multi-part review request, however, if you want to see the entire application, it can be found on GitHub. For this request, I'm looking to see how my EmailController is set up and how efficient you think it might be. I open to any and all suggestions about any facet of the code. This will be a long request as I couldn't figure out how to split up a lot of the logic into multiple requests without meaning being lost.
Keep in mind that this application is nearly to testing, however, there are a few pieces still left to do (a few templates, a few views, documentation).
EmailController
/**
* sendEmail
* Function mapped to Laravel route. Defines variable arrays and calls Email Class executeEmail.
*
* @param Request $request Request object passed via AJAX from client.
*/
public static function sendPhishingEmail(Request $request)
{
if(Auth::check()) {
$fromEmail = Campaign_Email_Addresses::where('EmailAddress',$request->input('fromEmailText'))->first();
$template = Template::where('FileName',$request->input('templateText'))->first();
$campaign = Campaign::where('Id',$request->input('campaignText'))->first();
if(!empty($fromEmail) && !empty($template) && !empty($campaign)) {
putenv("MAIL_USERNAME=$fromEmail->EmailAddress");
putenv("MAIL_NAME=$fromEmail->Name");
$cryptor = new Cryptor();
$password = $cryptor->decrypt($fromEmail->Password);
putenv("MAIL_PASSWORD=$password");
$templateClass = "\\App\\Mail\\$template->Mailable";
$sendingChoice = $request->input('sendingChoiceRadio');
if($sendingChoice == 'user') {
$user = Mailing_List_User::where('Id',$request->input('userIdText'))->first();
if(!empty($user)) {
Mail::to($user->Email,$user->FirstName . ' ' . $user->LastName)
->send(new $templateClass($user,$campaign,$request->input('companyText')));
self::logSentEmail($user,$campaign);
}
} else {
$group = MLU_Departments::where('Id',$request->input('groupIdText'))->first();
if(!empty($group)) {
$bridge = Mailing_List_User_Department_Bridge::where('DepartmentId',$group->Id)->get();
foreach($bridge as $pair) {
$user = Mailing_List_User::where('Id',$pair->UserId)->first();
if(!empty($user)) {
Mail::to($user->Email,$user->FirstName . ' ' . $user->LastName)
->send(new $templateClass($user,$campaign,$request->input('companyText')));
self::logSentEmail($user,$campaign);
}
}
}
}
}
}
return redirect()->route('generatePhish');
}
public static function sendNewAccountEmail(User $user, $password) {
if(Auth::adminCheck()) {
Mail::to($user->Email,$user->FirstName . ' ' . $user->LastName)
->send(new NewUser($user,$password));
}
}
public static function sendTwoFactorEmail(User $user, $code) {
Mail::to($user->Email,$user->FirstName . ' ' . $user->LastName)
->send(new TwoFactorCode($user,$code));
}
/**
* logSentEmail
* Logs to sent_email table info about this email and associated recipient.
*
* @param Mailing_List_User $user
*/
private static function logSentEmail(Mailing_List_User $user, Campaign $campaign) {
Sent_Mail::create(
['UserId'=>$user->Id,
'CampaignId'=>$campaign->Id,
'Timestamp'=>Carbon::now()]
);
}
Cryptor Library
protected $method = 'AES-256-CTR';
private $key;
protected function iv_bytes()
{
return openssl_cipher_iv_length($this->method);
}
public function __construct($key = false, $method = false)
{
if(!$key) {
$key = file_get_contents('../../' . getenv('CRYPTOR_SECRET_KEY'));
}
if(ctype_print($key)) {
$this->key = openssl_digest($key, 'SHA256', true);
} else {
$this->key = $key;
}
if($method) {
if(in_array($method, openssl_get_cipher_methods())) {
$this->method = $method;
} else {
die(__METHOD__ . ": unrecognised encryption method: {$method}");
}
}
}
public function encrypt($data)
{
$iv = openssl_random_pseudo_bytes($this->iv_bytes());
$encrypted_string = bin2hex($iv) . openssl_encrypt($data, $this->method, $this->key, 0, $iv);
return $encrypted_string;
}
public function decrypt($data)
{
$iv_strlen = 2 * $this->iv_bytes();
if(preg_match("/^(.{" . $iv_strlen . "})(.+)$/", $data, $regs)) {
list(, $iv, $crypted_string) = $regs;
$decrypted_string = openssl_decrypt($crypted_string, $this->method, $this->key, 0, hex2bin($iv));
return $decrypted_string;
}
return false;
}
Campaign Email Addresses Model
protected $table = 'campaign_email_addresses';
protected $primaryKey = 'EmailAddress';
public $incrementing = false;
protected $fillable = ['EmailAddress',
'Name',
'Password'];
public static function insertEmail($email, $name, $password) {
$cryptor = new Cryptor();
$encrypted = $cryptor->encrypt($password);
unset($password);
$query = self::where('EmailAddress',$email)->first();
if(count($query)) {
throw new DuplicateKeyException("Email Address already exists.");
}
return self::create([
'EmailAddress'=>$email,
'Name'=>$name,
'Password'=>$encrypted
]);
}
public static function updateEmail($email, $name, $password) {
$cryptor = new Cryptor();
$encrypted = $cryptor->encrypt($password);
unset($password);
$query = self::query();
$query->where('EmailAddress',$email);
$query->update(['Password'=>$encrypted,'Name'=>$name]);
return $query->get();
}
public static function decryptPassword($email) {
$cryptor = new Cryptor();
$password = self::where('EmailAddress',$email)->first()->Password;
return $cryptor->decrypt($password);
}
Template Model
NOTE: Templates are all stored in the same location, database is just to display available templates to user. Emails are sent using Mailables.
protected $table = 'templates';
protected $fillable = ['EmailType',
'FileName',
'PublicName',
'Mailable'
];
protected $primaryKey = 'FileName';
public $incrementing = false;
Mailing List User Model
protected $table = 'mailing_list';
protected $primaryKey = 'Id';
protected $fillable =
['Email',
'FirstName',
'LastName',
'UniqueURLId'
];
public static function updateMailingListUser($mlu, $email, $fname, $lname, $uniqueURLId = '') {
$query = Mailing_List_User::query();
$query->where('Id',$mlu->Id);
$update = array();
if(!empty($email)) {
$update['Email'] = $email;
}
if(!empty($fname)) {
$update['FirstName'] = $fname;
}
if(!empty($lname)) {
$update['LastName'] = $lname;
}
if(!empty($uniqueURLId)) {
$update['UniqueURLId'] = $uniqueURLId;
}
$query->update($update);
$query->get();
}
Mailing List Departments Model
protected $table = 'mailing_list_departments';
public $timestamps = false;
protected $primaryKey = 'Id';
protected $fillable =
['Department'];
Mailing List User and Department Bridge Model
protected $table = 'mailing_list_users_departments_bridge';
protected $primaryKey = ['UserId','DepartmentId'];
public $incrementing = false;
public $timestamps = false;
use CompositeKeyTrait;
protected $fillable =
['UserId',
'DepartmentId'
];
Campaign Model
protected $table = 'campaigns';
protected $primaryKey = 'Id';
protected $fillable = ['Name',
'Description',
'Assignee',
'Status'];
public static function updateCampaign($campaign,$description,$assignee,$status) {
$query = Campaign::query();
$query->where('Id',$campaign->Id);
$update = array();
if(!empty($description)) {
$update['Description'] = $description;
}
if(!empty($assignee)) {
$update['Assignee'] = $assignee;
}
if(!empty($status)) {
$update['Status'] = $status;
}
$query->update($update);
$query->get();
}
public static function getAllActiveCampaigns() {
return Campaign::where('Status','active')->get();
}
Sent Mail Model
protected $table = 'sent_email';
public $timestamps = false;
protected $primaryKey = 'Id';
protected $fillable = ['UserId',
'CampaignId',
'Timestamp'];