Say I've got a method in c# MVC to send email with ajax, like:
public class mailController : Controller {
SmtpClient mailserver = new SmtpClient("smtp.foo.com");
public string send(string from, string to, string subject = "", string body = "", string cc = "", string bcc = "") {
MailMessage message = new MailMessage(from, to, subject, body);
if (cc.Length > 0) {
message.CC.Add(cc);
}
if (bcc.Length > 0) {
message.Bcc.Add(bcc);
}
mailserver.Send(message);
return "MessageSent";
}
}
Is there anything I can do to make this more secure? I mean, as it stands anyone can type the relevant info into their address bar. http://www.foo.com/mail/send?from=etc If I want to use this for form submission, I can't password protect it, or I'd have to use that in the javascript, which is easy to find. I considered setting a cookie and using that as authentication, but that only goes so far. Is there a standard procedure for protecting ajax methods?
You need to validate on the server that the parameters are what you want them to be.
You need to implement a secure session token, to prevent unauthorized users (those without valid sessions) from being able to send an email. This is basically no different than any other cross site request forgery (CSRF) attack vector. If you need any additional information just Google 'CSRF protection ASP.NET`' or similar to get some more concrete examples of how to do this.
Your requirements sound mutually exclusive.
If you do want to leave it public, but you don't want it abused, then maybe you could provide some sort of throttle where you only allow x number of requests from a specific IP address.
You can also use mailto: in an HTMLform to prompt the client to send the email.
As a start you can always store the IP that accessed your controller, if same IP is trying to send mail in specific frequency that you define you can deside to block it ot whatever...
at second you can generate a random number in your mailing page that will be send to the controller -> this will allow you to verify that the mail is sent from your site and not from third party
Related
I am trying to send a mail in Production with a verification link for a user registration.
For this, I have attached the credentials of the gmail account that sends the mail in my appsettings.json
APPSETTINGS.JSON:
The action of my controller that sends the mail is the following:
[HttpPost]
public async Task<JsonResult> Register(AddUserViewModel model)
{
if (ModelState.IsValid)
{
User user = await _userHelper.AddUserAsync(model, imageId);
if (user == null)
{
return Json("Email repeat");
}
string myToken = await _userHelper.GenerateEmailConfirmationTokenAsync(user);
string tokenLink = Url.Action("ConfirmEmail", "Account", new
{
userid = user.Id,
token = myToken
}, protocol: HttpContext.Request.Scheme);
Response response = _mailHelper.SendMail(model.Username, "App - ConfirmaciĆ³n de cuenta", $"<h1>App - ConfirmaciĆ³n de cuenta</h1>" +
$"Para habilitar el usuario, " +
$"por favor hacer clic en el siguiente enlace: </br></br>Confirmar Email");
if (response.IsSuccess)
{
return Json("Email send");
}
string message = response.Message;
return Json(message);
}
return Json("Model invalid");
}
The sendEmail method that returns a Response is as follows:
public Response SendMail(string to, string subject, string body)
{
try
{
string from = _configuration["Mail:From"];
string smtp = _configuration["Mail:Smtp"];
string port = _configuration["Mail:Port"];
string password = _configuration["Mail:Password"];
MimeMessage message = new MimeMessage();
message.From.Add(new MailboxAddress(from));
message.To.Add(new MailboxAddress(to));
message.Subject = subject;
BodyBuilder bodyBuilder = new BodyBuilder
{
HtmlBody = body
};
message.Body = bodyBuilder.ToMessageBody();
using (SmtpClient client = new SmtpClient())
{
client.CheckCertificateRevocation = false;
client.Connect(smtp, int.Parse(port), false);
client.Authenticate(from, password);
client.Send(message);
client.Disconnect(true);
}
return new Response { IsSuccess = true };
}
catch (Exception ex)
{
return new Response
{
IsSuccess = false,
Message = ex.Message,
Result = ex
};
}
}
The error message is the following:
"534: 5.7.14
\u003Chttps://accounts.google.com/signin/continue?sarp=1\u0026scc=1\u0026plt=AKgnsbt\n5.7.14
GCim6bqtaJeANyyQ0NvegYJS8qnYbDSCz3M0IMvB-rgIFdr1rLrIl1wbt4DkimTvNMLDl\n5.7.14
8dSGZxGuAWmDwX6gPD1T_lJ3U1e0G8EEAu6Lgt3p5gk1yJpr85Pm2mBN9nO4G33Y\u003E\n5.7.14
Please log in via your web browser and then try again.\n5.7.14 Learn
more at\n5.7.14 https://support.google.com/mail/answer/78754
t15sm12262168pjy.17 - gsmtp"
Am I sending correctly?
Should I change any gmail settings?
In the development environment the sending works without problems, any help or suggestion for me?
Directly using providers like Gmail, Outlook and similar ones is not advised because they perform checks that can disrupt your code just like you're seeing here.
This can happen without previous notice. And believe me, it will happen from time to time.
Common issues
Web-based OAuth flow
Most providers (Gmail specially) now enforce a web-based flow, from the most recent OAuth/OpenID specifications, for safer authentication/authorization.
This would be my first guess: Gmail auth (which is browser based) is simply blocking your login attempt because it wants you to use a browser and not simply submit your credentials.
There's a lot of background work done by IAM solutions to help protect users, namely called risk assessment. This, in time, might require both captcha and MFA challenges to be sent back to the user, so an API would not really work on this, that's another reason why they focus on browsers and not simply on getting the correct credentials.
Bot prevention
Email providers (specially Gmail) are great at detecting possible bots and this would be my second guess: it detected your service as a bot and put a temporary hold on your account to "protect you".
It's possible that this works on lower environments (aka: your machine and/or testing environment) and not production because of the server bot prevention system, which typically inspects IP address, user agent from the browser (if any), API call rate, authentication rate and others.
Usage rate limit
Yet another thing that can block you when doing such integration is the rate limit. Typically, 500 messages/month.
Possible solutions
Workaround - still using Gmail
To workaround this and still use Gmail, this is the way to go:
https://support.google.com/accounts/answer/185833?hl=en
It's called application password and is a different password that you generate on your account for a particular app to be allowed signing in. That will skip the OAuth and the bot validation (at least on most cases).
Proper long-term solution
The only reliable way to have this fixed is to use a proper send email service. Something like Amazon Simple Email Service, Sendgrid, or a bunch of others out there.
That's because they are created to be used by API calls and won't get in your way. You can set-up your own domain there, including SPF and DKIM keys so your recipient's email server will recognize the message as safe and indeed as coming from your (and not a random SPAM).
I have a very simple email form that sends out the email using department email account to given recipients. However, the company makes the password expire every 90 days, and there's a web form that I created where the authorized user can go and change the password/email address for the form.
Here's the code:
SmtpClient smtp = new SmtpClient();
smtp.Host = host;
smtp.UseDefaultCredentials = false;
smtp.Credentials = new System.Net.NetworkCredential(username, password);
try
{
smtp.Send(iEditMail);
}
catch(SmtpException exception)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, exception.Message + " Make sure the username and password is correct.");
}
Since user is responsible for entering the password, they may accidentally enter the wrong password for the email account or the password may expire. I need to be able to catch this so it can be notified to users who are trying to send email. (Btw, this is only for departmental use). How can I make sure if the credentials are correct before sending the email. Right now it doesn't throw any error to tell the email wasn't sent - we just don't receive it.
First:
If you can, it is better to create a service account and use those credentials. That way there is no need to change the credentials periodically. (#mason's suggestion)
However, if that is not possible:
According to MSDN, the SmtpClient.Send function throws SmtpException if authentication failed. That stinks because that same exception can mean various other things too. But it sounds like you want to verify the user name and password without sending an email. The SMTP protocol certainly allows that, but the SMTPClient class only provides methods for sending email. I don't know of any built-in .NET class that provides what you want.
So I think you need to either
Write your own SMTP code using TCP.
Find a third-party SMTP client that provides more functionality..
Send a dummy email to a dummy address, and look at the StatusCode property to see if there was an authentication problem.
Option number 1 is hard if you care about SSL. If you just use plain text, I think authenticating to SMTP isn't that hard. But more and more mail servers are forbidding that. Option 3 is a hack, but should be easy.
EDIT: Added details about the StatusCode property.
The SMTPClient's Send(MailMessage) method raises a SmtpFailedRecipientsException exception if one or more recipient
addresses were incorrect or unreachable.
Sample Code :
try
{
smtpClient.Send(new MailMessage("test#test.com", "test#test.com", "test", "test"));
return string.Empty;
}
catch (SmtpFailedRecipientException)
{
return string.Empty;
}
catch (Exception ex)
{
return string.Format("SMTP server connection test failed: {0}", ex.InnerException != null ? ex.InnerException.Message : ex.Message);
}
When authenticating, store the username and password in the session. Compare the values of the session with user entries . If bool==true then send the email or else dont.
I am creating a free program that has a support request page. This will send me an email and a log file so I can see what is going on.
I have created the email class, but like any email program/class it will require a username, password, email address, etc.
Now, once I put this out, I know that, with the right programs, you can view the code behind on .Net, which I really thing is completely absurd.
Anyhow, I don't want this information out there because it's a support email address and is sensitive information.
What is the best way for me to send an email but not include my sensitive information for all the nosy people out there or hide it so they cannot get to it?
Here is an example of the code I'm referring to:
var emailSettings = new EmailSettings();
emailSettings.Body = richTextBox_Message.Text;
emailSettings.BodyIsHtml = false;
emailSettings.EmailServerEnableSsl = true;
emailSettings.EmailServerPassword = "";
emailSettings.EmailServerPort = 25;
emailSettings.EmailServerUsername = "";
emailSettings.EmailSmtpServer = "";
emailSettings.FromEmailAddress = new MailAddress(textBox_EmailAddress.Text);
emailSettings.Subject = comboBox_TypeOfRequest.Text;
Scary stuff in there :D
Just to clarify, this is a free app so I cannot afford a program to hide the code. :(
It's not safe to use this method to send error reports. Consider using a .php file on a web server to send yourself errors, or some other method.
PHP Email Tutorial
But if you really want to, I believe you might be able to use System.Security.Cryptography to protect your data, I'm not 100% sure on that though, so correct me if I'm wrong.
http://msdn.microsoft.com/en-us/library/system.security.cryptography(v=vs.110).aspx
I want to be able to send messages to a specific client, I have working code but I cannot find a way to identify users, as on each page load/refresh the client id will change, so I cannot rely on that.
I have tried to append a querystring to the connection, but I have only been able to find the querystring of the same context.
This is my hub, and within the send method i want to be able to match the id that is sent in to a particular connection id at the point of sending the message:
public class Chat : Hub
{
public string addMsg()
{
return "";
}
public void Send(string message, string id)
{
Clients.Client(Context.ConnectionId).receiveMessage(message);
}
}
Here is my client code, i want to pass the id of the person to send a message to to the server send method, and use the querystring value of the other connected user to match it to the id i am sending.
var chat = $.connection.chat;
chat.client.receiveMessage = function (message) {
alert("Received from server: " + message);
};
chat.addMsg = function (message) {
};
$("#sendMessage").click(function () {
chat.server.send($('#newMessage').val(), 6);
});
$.connection.hub.qs = "id=1";
$.connection.hub.start().done(function () {
var myClientId = $.connection.hub.id;
var qs = $.connection.hub.qs;
});
I hope my question makes sense, I have been trying to crack this for a while now, below are some links to some of the articles i have used to get to where i am now, I am just missing the last piece of the puzzle, please go easy on me :)
http://weblogs.asp.net/davidfowler/archive/2012/11/11/microsoft-asp-net-signalr.aspx
SignalR- send data to a specific client
https://github.com/SignalR/SignalR/wiki/QuickStart-Hubs
I don't think this is going to work the way you want it to. The connection ID is just that -- an identifier for a particular connection. SignalR itself doesn't know anything about authenticating users. It is, however, built on top of ASP.NET and all of your familiar authentication methods (Windows, Forms, etc.) work as you would expect.
Once ASP.NET has authenticated the user, you have access to this in your hubs as Context.User. It's now up to you to maintain a mapping between this user and one or more connection IDs. Besides browser refreshes, you might need to deal with a user accessing your service from multiple browsers or machines. Sending a message to this user means sending it to all of those browsers and machines.
Jabbr does all this and more. You really should take a look at that code for a good way to implement this.
How about using the Clients.Caller object int he hub, and overriding the OnConnected method:
public override Task OnConnected()
{
Clients.Caller.sendInitMessage(...);
return base.OnConnected();
}
I'm thinking of implementing "Report a bug/Suggestions" option to my game, however I am not quite sure how I could get that working. I do not have my own server or anything, so I can't just send the text that user has written to there.
The only way I came up with is that the client would write a message and I would send it to an email account where I could read them. However, I do not want that users would need to send the reports through their personal accounts. I am not quite sure how I could implement this and googling didn't bring up any good suggestions.
I haven't done a lot of network stuff, so I'd really appreciate it if you could explain ( possibly even in code ) the process step-by-step.
I am using C# and the game is being programmed for Windows Phone 7.
Yes, it is absolutely possible to do that. From a relatively low-level perspective, you need to:
Resolve the MX (mail-exchanger) server for the e-mail account you want to send to.
Open a socket to the MX server.
Send the appropriate SMTP commands to cause the e-mail message to be delivered to your recipient account. You essentially have the freedom to set the "from" address to be any arbitrary thing you want.
SMTP is a very simple/human-friendly protocol, so it's not a massive effort to do all of that by hand. At the same time, there are prebuilt libraries that will handle all of that for you (except possibly the resolution of the recipient's MX server).
Note that emails sent this way are more likely to be filtered out as spam (generally because the sender's IP/hostname is not going to match whatever domain you put on the outgoing e-mail address you decide to use).
Also note that since you can set the "from" address to anything, you have the option of asking the user if they want to provide their actual contact address, and if they do you can make that the "from" address so that you can actually get back in touch with them if necessary.
You don't need to use email at all. Consider using an error reporting service like sentry or airbrake.
These services have clients that you embed in your program; which automatically log your errors, including any debugging information/stacktrace; and notify you by email when your application reports a problem.
Usually you integrate the app's API into your own error handling mechanism. At the point of an error, the client will capture your debugging information, you can popup a modal asking user for information like "what were you doing when this error happened?", save that as part of your error response that is sent back to the service.
Since the app works over HTTP, you don't need any special ports to be open. It is easier and more helpful than having users send you emails with "it doesn't work!!", and you don't have to deal with email sending headaches.
I recently wrote an article on this: Sending email with C#
You basically have two choices, either you send it using an SMTP-client, this means that you have to have a SMTP-server and be able to connect to port 25 (if you're not using an external SMTP, then you have to manage that by yourself). Or you can use an external email provider, such as:
AlphaMail
SendGrid
Mandrill
If you're using AlphaMail you can send emails in the following way:
IEmailService emailService = new AlphaMailEmailService()
.SetServiceUrl("http://api.amail.io/v1/")
.SetApiToken("YOUR-ACCOUNT-API-TOKEN-HERE");
var person = new Person()
{
Id = 1234,
UserName = "jdoe78",
FirstName = "John",
LastName = "Doe",
DateOfBirth = 1978
};
var response = emailService.Queue(new EmailMessagePayload()
.SetProjectId(12345) // ID of AlphaMail project (determines options, template, etc)
.SetSender(new EmailContact("support#company.com", "from#example.com"))
.SetReceiver(new EmailContact("Joe E. Receiver", "to#example.org"))
.SetBodyObject(person) // Any serializable object
);
Another thing that differs from just building HTML and sending it with an SMTP-client is that with AlphaMail you have the ability to edit your emails outside your code directly in a GUI. You can also easily create highly dynamic templates using AlphaMail's templating language Comlang.
<html>
<body>
<b>Name:</b> <# payload.FirstName " " payload.LastName #><br>
<b>Date of Birth:</b> <# payload.DateOfBirth #><br>
<# if (payload.Id != null) { #>
Sign Up Free!
<# } else { #>
Sign In
<# } #>
</body>
</html>
So this is my thought, why don't you have the email sent to you...as you?
using System.Net;
using System.Net.Mail;
var fromAddress = new MailAddress("from#gmail.com", "From Name"); //Both the email addresses would be yours
var toAddress = new MailAddress("to#example.com", "To Name"); //Both the email addresses would be yours
const string fromPassword = "fromPassword";
const string subject = "There name or whatever";
const string body = "Errors ect....";
var smtp = new SmtpClient
{
Host = "smtp.gmail.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
};
using (var message = new MailMessage(fromAddress, toAddress)
{
Subject = subject,
Body = body
})
{
smtp.Send(message);
}
code from here
All they would see would be the submit button so they wouldn't have all your personal username/password, also you should prolly set up a dummy account to have them sent to even if it just then forwards them to your real email account.
Another way to achieve this would be to host a WCF Service which takes in your Message and stores in db or /sends email. One downside of this is you'll need a web server to do this.
Try following code this might help you :
Dim objCDOMail
Set objCDOMail = Server.CreateObject("CDONTS.NewMail")
objCDOMail.From = "sender#domain.com"
objCDOMail.To = "receiver#domain.com"
objCDOMail.Subject = "Test Mail Script"
objCDOMail.BodyFormat = 0
objCDOMail.MailFormat = 0
objCDOMail.Body = "Testing Mail from Test Script"
objCDOMail.Importance = 1
objCDOMail.Send
Set objCDOMail = Nothing