Saturday, April 17, 2021
  • Setup menu at Appearance » Menus and assign menu to Top Bar Navigation
Advertisement
  • AI Development
    • Artificial Intelligence
    • Machine Learning
    • Neural Networks
    • Learn to Code
  • Data
    • Blockchain
    • Big Data
    • Data Science
  • IT Security
    • Internet Privacy
    • Internet Security
  • Marketing
    • Digital Marketing
    • Marketing Technology
  • Technology Companies
  • Crypto News
No Result
View All Result
NikolaNews
  • AI Development
    • Artificial Intelligence
    • Machine Learning
    • Neural Networks
    • Learn to Code
  • Data
    • Blockchain
    • Big Data
    • Data Science
  • IT Security
    • Internet Privacy
    • Internet Security
  • Marketing
    • Digital Marketing
    • Marketing Technology
  • Technology Companies
  • Crypto News
No Result
View All Result
NikolaNews
No Result
View All Result
Home Technology Companies

Configure multifactor authentication for IBM Cloud Node.js applications

January 12, 2019
in Technology Companies
Configure multifactor authentication for IBM Cloud Node.js applications
589
SHARES
3.3k
VIEWS
Share on FacebookShare on Twitter

Credit: IBM

Use risk analysis and enhancements for more security

You might also like

How AI helps Overwatch League process 410M data points to build power rankings – IBM Developer

A brief intro to Red Hat OpenShift for Node.js developers – IBM Developer

IBM joins Eclipse Adoptium and offers free certified JDKs with Eclipse OpenJ9 – IBM Developer

Ori Pomerantz
Published on February 15, 2016/Updated: March 16, 2018


Comments

In this tutorial, you learn how to configure two-factor authentication for
your IBM Cloud Node.js application. Sending a separate token to the
user’s email address makes masquerading as that user a lot harder. A
potential attacker would not only have to steal the password, but would
also need to hack into the mail server to get the token.

In addition, this tutorial teaches you some techniques for risk analysis.
By analyzing risk, an application can determine when an attempt to log in
is risky. It will require a second factor for authentication only in such
cases.

What
you’ll need to build your application

Run the appGet the code

“ In this tutorial, you learn how to use a random string
delivered by email as a second authentication factor. I also discuss
several methods for risk analysis.”

Why
use two-factor authentication for risky logins?

Passwords have two main failure modes:

  • Inadvertent disclosure: An attacker discovers the password of an
    authorized user.
  • Password sharing: An authorized user gives the password to somebody
    else, usually to enable that person to use some of the authorized
    user’s permissions.

Both of these failure modes can be fixed by requiring authorized users to
prove that they have access to their email as a second authentication
factor. The second factor can be required each time, or only when a
transaction appears dangerous and requires extra security.

Get started

Follow these steps to create a new Node.js application on IBM Cloud:

  1. Log on to IBM Cloud. Create a free account if you do not
    have one.
  2. Click on the menu icon and select Cloud Foundry Apps.
    this is how you get to the platform as a service (PaaS) offerings in
    IBM Cloud.
  3. Click Create Cloud Foundry app.
  4. Click SDK for Node.js.
  5. Type an app name (for example, mfa-app) and a hostname (I
    chose mfa-app, so you will have to choose something
    else). Then click Create.
  6. Wait for the application to start.

Configure the web IDE

We could develop the application on our own system, but I prefer the
web-based IDE.

  1. When the application page opens, click Overview on
    the left sidebar, scroll down to the Continuous delivery heading and
    click Enable.
  2. Scroll down and specify the repository type Clone and
    the source repository URL
    https://git.ng.bluemix.net/qbzzt1/mfa-app,
  3. Click Create.
  4. Once the tool chain is created, click Eclipse Orion Web
    IDE
    to edit the application files.
  5. Open manifest.yml and change the name and host from
    two-factor authentication to the values you entered when you created
    the application (step 5 under ).
  6. Click the play button icon to deploy the application with the new
    manifest.

Authentication

First, you need an authentication workflow that uses the second factor
authentication. To do this, you send emails with long random strings. Only
the legitimate user, or others with access to that user’s email, can
access these values.

Create random strings

The easiest way to create long random strings is to download and use the node-uuid package,
which creates RFC 4122 identifiers. Each of those identifiers has 60
random bits, which are enough for all practical purposes. The identifiers
are encoded as text. These are the steps to create a UUID object (you can
also see them in the source code).

  1. To use node-uuid, add a dependency on node-uuid (at any version) to
    package.json:

    "dependencies": {
        "express": "4.12.x",
        "cfenv": "1.0.x",
        "body-parser": "*",
        "node-uuid": "*"
    	},

  2. Then, create a uuid object:
    // Use uuid to generate random strings.
    var uuid = require("node-uuid");

Send out messages

To send out email messages, use the SendGrid service in IBM Cloud. First, you
need to create and bind the service and obtain an API key:

  1. Log in to IBM Cloud and click on your application in the dashboard.
  2. Click Overview on the left sidebar.
  3. Scroll down to the Connections and click Connect
    new.
  4. Select the Application Services category of the
    catalog and click the SendGrid service.
  5. Select a package and click Create.
  6. If you are prompted, click Restage.
  7. Click the menu icon and select Application Services.
    Then click on the SendGrid service that you created.
  8. Click Open SendGrid Dashboard.
  9. From the SendGrid site, click Settings > API Keys
    on the left sidebar.
  10. Click Create API Key.
  11. Name the key, select Full Access, then click
    Create and View.
  12. Copy this API key to the clipboard:

    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

Use SendGrid to send an email

  1. Return to the IBM Cloud console. Go to the application that you created,
    scroll down, and click View toolchain.
  2. Add a dependency on SendGrid (at any version) to package.json (which
    is already done in the sample application):

    "dependencies": {
        "express": "4.12.x",
        "cfenv": "1.0.x",
        "body-parser": "*",
        "node-uuid": "*",
        "sendgrid": "*"
    	},

  3. Create a SendGrid object by using the API key that you received and
    use it to send email. Do it once from the main code of app.js, rather
    than a handler, to verify that everything works.

    Note:
    When you start by cloning the application, you just need
    to change the API Key in app.js, on line 41.

    // Use SendGrid to send emails as a second token.
    var sendgrid = require("sendgrid")("API_KEY goes here ");
      
    // Send an email
    var email = new sendgrid.Email();
    
    email.addTo("unmonitored@my.app");
    email.setFrom("qbzzt1@gmail.com");
    email.setSubject("");
    email.setHtml("<H2>Big test</H2>");
    
    sendgrid.send(email);

You should receive the email in a minute or two. If not, make sure to look
in your spam folder. Many filters consider email like this to be spam.

Putting it all together: The
authentication workflow

Users register and log in by filling out different forms on index.html.
Their information is sent in a POST request to the server. The rest of
this section explains the login flow; the registration flow is very
similar.

The user attempts to log in

First, the code checks if the email and password pair is even valid. The
users are stored in a hash table, where the key is the user’s email
address. If the user does not exist, or if the password is wrong, the
application returns to the user an error message. It is the same message
whether the user is nonexistent or the password is wrong. This avoids
unintentionally revealing whether an email address belongs to a valid
user.

var user = users[req.body.email];
  
if (!user) {
  	res.send("Bad user name or password.");
  	return ;
}
  
if (user.password !== req.body.passwd) {
  	// Same response, not to disclose user identities
  	res.send("Bad user name or password.");
  	return ;  	
}

Note that storing users’ records in a hash table like this is simple, and
is therefore ideal for sample programs such as this one. But it is not a
good idea to delete all the users whenever you restart the application in
production, or to have different instances of the application have
different user lists. In production, you should use the Cloudant DB.

If the user and password match, check if the user is still pending. If so,
this is also an error condition. You can add a link to resend the
confirmation email to the message sent to the user.

// User exists, but email not confirmed yet
if (user.status === "pending") {
  	res.send("Account not confirmed yet.");
  	return ;
}

Assuming that everything checks out, the next step is to create a
request.

// Create request to confirm the logon
var id = putRequest(req.body.email);

The putRequest function starts by creating a random identifier
as explained earlier.

// Register a pending request for this email
var putRequest = function(email) {
   // Get the random identifier for this request
   var id = uuid.v4();

Next, it adds the request to the pendingReqs hash table with
that identifier. The request includes the identity of the requesting user.
It also gets a time stamp, to allow you to clean up old requests that are
abandoned. As noted above in regard to the email/password pair, in a
production application, the pendingReqs hash table should be
a database instead.

   pendingReqs[id] = {
   	email: email,
   	time: new Date()
   };

The function that called putRequest needs to inform the user
of the ID so the user can verify that the request is legitimate.
Therefore, putRequest returns the ID to the caller.

   return id;
};

The application sends a token by
email

After putRequest, the handler calls a function to send the
user an email and responds to the user.

  // E-mail the account confirmation request
  sendLoginRequest(req.body.email, id);
  
  res.send("Thank you for your request. Please click the link you will receive by email to " +
    req.body.email + " shortly.");	
});

The sendLoginRequest function composes an HTML message and
sends it to the user. There are two variables in the message text. The
first, appEnv.url, is the URL used to access the application.
This is necessary because a relative link won’t work in an email that
doesn’t have the context of the web browser’s last URL. The second is the
ID of the request to be approved. Taken all together, the URL in the
message is <appEnv.url>/confirm/<id>. This
is the URL where you will get the confirmation if the email address is
correct.

// Send a link. Standard practice is to send a code, but using a link
// is easier and more secure.
var sendLoginRequest = function(email, id) {
  
  // Send an email
  var msg = new sendgrid.Email();

  msg.addTo(email);
  msg.setFrom("notMonitored@nowhere.at.all");
  msg.setSubject("Application log in");
  msg.setHtml("<H2>Welcome to the application</H2>" +
  	'<a href="' + appEnv.url + '/confirm/' + id + '">' +
  	'Click here to log in</a>.');
  	
  sendgrid.send(msg);

};

Note that this is different from the standard practice, which is to provide
a short (4-6 characters) code in the email for the user to type into a web
form. I prefer this method because it is easier and allows for more
possible keys. The disadvantage is that anybody who can access the email
can break into the application. In the “http://www.ibm.com/Keep safe from
email sniffers
” section near the end of this tutorial, I discuss
how to solve that problem.

The user logs in with the
emailed token

The email directs the user to a URL at the path
confirm/<id>. This call is processed by the
code below. The :id string means it can be any valid path
component, and the value will be available in
req.params.id.

// A confirmation (of an attempt to register or log in)
app.get("/confirm/:id", function(req, res) {

The first thing to do is to retrieve the request that is being confirmed
and delete it. If there is no such request, report the error to the
user.

	var userRequest = pendingReqs[req.params.id];
	delete pendingReqs[req.params.id];

    // Meaning there is no user request that matches the ID.
    if (!userRequest) {
    	res.send("Request never existed or has already timed out.");
    	return ;   // Nothing to return, but this exits the function    	
    }

The object for every request includes the email address that identifies the
user. This lets you retrieve the user information.

	var userData = users[userRequest.email];

If the user is pending, it means that this is a confirmation of the
account.

if (userData.status === "pending") {
    userData.status = "confirmed";
        res.send("Thank you " + userRequest.email + " for confirming your account.");    	
		return ;
}

If the user account is already confirmed, then this is a confirmation of
the second factor for authentication.

	// In a real application, this is where we'd set up the session and redirect
	// the browser to the application's start page.
	res.send("Welcome " + userRequest.email);
});

In a real application, this is where you would create a session and put a
session cookie in the browser. To learn how to do that on Node.js, refer to the tutorial “http://www.ibm.com/Use LDAP and Active Directory to authenticate Node.js users.”

Note: This account is somewhat simplified. When SendGrid receives
an email to send, it replaces the links with links to its own site, where
the browser is redirected to the original URL. This allows SendGrid to
provide statistics for links accessed through email. In the case of the
following illustration, you see that on Thursday, SendGrid sent 13
messages, and got nine clicks, to seven unique URLs.

Screen shot showing statistics     overview

Screen shot showing statistics overview

Risk analysis

It is possible to require two-factor authentication every time a user logs
in. However, that is considered user hostile. It is much better from a
usability perspective if the application evaluates the chance that a login
attempt is illicit and use that information to decide whether requiring a
second factor is warranted.

It is important that this decision be based on factors that are difficult
to forge. For example, the type and version of the browser is very easy to
fake in an HTTP header. It is much harder, though, to fake IP addresses
(because you need the response routed to you) or the time of access.

Client IP address

Browsers do not access IBM Cloud directly, but through IBM WebSphere DataPower Appliances acting as proxies. To obtain
the client IP address, rather than that of the proxy, the application has
to trust the proxy. You set this using app.set:

// Necessary to know the IP of the browser
app.set("trust proxy", true);

The IP address from which a request arrives is available in
req.ip. Here it is in use:

// Show the user's IP address
app.get("/ip.html", function(req, res) {
	res.send("<H2>Your IP address is</H2>" + req.ip);
});

To see the result, browse to http://two-factor-auth.mybluemix.net/ip.html.

Interpreting the IP
address

To use the IP address, you need to interpret it. One easy-to-use database
of IP addresses is http://ipinfo.io. You
can go to http://ipinfo.io/<ip address> to get complete
information, or http://ipinfo.io/<ip
address>
/<field> to get a specific field, such
as the country.

To learn how to send an HTTP request and receive a response from the
application, see Step 3 in “http://www.ibm.com/Build a self-posting Facebook application with IBM Cloud and the MEAN
stack, Part 3
.” Here is the code used in this application:

// The library to issue HTTP requests
var http = require("http");

Because Node.js is single threaded, and the result will be available only
after the request gets to ipinfo.io and the response comes back, use a
next() function that is called when the result is
available.

// Interpret an IP address and then call the next function with the data
var interpretIP = function(ip, next) {

The http.get function receives a URL and a callback function.
It then gets the URL from its server.

	http.get("http://ipinfo.io/" + ip,

This callback function is called as soon as you get the HTTP headers. But
the data you need is provided in the HTTP body of the response. Therefore,
you need to wait until you receive data.

		function(res) {

This code registers a handler for a data event. Because the response is so
short, it can be assumed to come in a single chunk. If there were multiple
chunks, you would concatenate them together until you got an end
event.

			res.on('data', function(body) {

When you don’t access it from a browser, ipinfo.io helpfully provides the
data in a JSON object, which is easy to parse.

				var data = JSON.parse(body);
				next(data);
			});
		}
	);

};

// Show the user's IP address
app.get("/ip.html", function(req, res) {
	interpretIP(req.ip, function(ipData) {
		var resHtml = "";
		resHtml += "<html><head><title>IP interpretation</title></head>";
		resHtml += "<body><H2>Intepretation of " + req.ip + "</H2>";

To show the result, put all the data fields in a table.

		resHtml += "<table><tr><th>Field</th><th>Data</th></tr>";
		for (var attr in ipData) {
			resHtml += "<tr><td>" + attr + "</td><td>" + ipData[attr] + "</td></tr>";
		}
		resHtml += "</table></body></html>";
		res.send(resHtml);
	});
});

To see the result for your own IP address, browse to https://two-factor-auth.mybluemix.net/ip.html.

Time and day of the week

Getting the time and day of the week is very simple. Just create a new Date object. The days of the week start with 0 as Sunday and 6 as
Saturday; the hour is 0-23. However, the time zone is UTC, the London time
zone (without daylight savings time). This means, for example, that for
CST in the US you need to deduct 6 hours.

Usually, the risk depends on whether the time can be classified as business
hours, evening, or weekend. This is the code that handles that:

// Classify time as "day", "after hours", or "weekend". The time zone
// is the difference in hours between your time and GMT.
var classifyTime = function(timeZone) {
	var now = new Date();
	
	// Hour of the week, zero at a minute after midnight, on Sunday
	var hour = now.getDay()*24 + now.getHours() + timeZone;

	// If the hour is out of bounds because of the time zone, return it
	// to the 0 - (7*24-1) range.
	if (hour < 0)
		hour += 7*24;
	
	if (hour >= 7*24)
		hour -= 7*24;
		
	// The weekend lasts until 8am on Monday (day 1) and starts at 5pm on
	// Friday (day 5)
	if (hour < 24+8 || hour >= 5*24+17)
		return "weekend";
		
	// Work hours are 8am to 5pm
	if (hour % 24 >= 8 && hour % 24 < 17)
		return "day";
	
	// If we get here, it is after hours during the work week
	return "after hours";
};



// Show the current time and day of the week
app.get("/now.html", /* @callback */ function(req, res) {
	var now = new Date();
	
	var resHtml = "";
	resHtml += "<html><head><title>Present Time</title></head>";
	resHtml += "<body><H2>Present Time</H2>";
	resHtml += "Day of the week (UTC): " + now.getDay() + "<br />";
	resHtml += "Hour (UTC): " + now.getHours() + "<br />";
	resHtml += "Time classification CST:" + classifyTime(-6) + "<br />";
	resHtml += "</body></html>";
	
	res.send(resHtml);
});

To see the current result for CST click here.

Show an example of risk
analysis

The problem with using risk analysis in a sample application is that it can
be an annoyance to check the parameters. You would want to see the results
for multiple countries and multiple times, without traveling or waiting.
Therefore, the risk page lets you manually specify the time classification and
the IP address.

Screenshot of risk page

Screenshot of risk page

Risk analysis policy

Using two parameters—IP address and time classification—you can set up a
policy to decide what to do. For example, you might decide that logins
from the US are expected only during business hours, logins from China are
expected at any time except the weekend (because their working hours are
very different), and you never expect users to log in from anywhere
else.

It is trivial to implement such a policy in code:

// Decide the risk level
app.post("/risk", function(req, res) {
	interpretIP(req.body.ip, function(ipData) {
		var country = ipData.country;		
		var time = req.body.time;
		var resHtml = "";
		var safe = false;
		
		resHtml += "<html><head>";
		resHtml += '<link rel="stylesheet"  ' +
			'href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">';
   		resHtml += '<link rel="stylesheet" ' +
			'href="http://www.ibm.com/https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/' +
			'css/bootstrap-theme.min.css">';
   		resHtml += '<script ' +  	
			'src="http://www.ibm.com/https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js">' +
			'</script>';
   		resHtml += "</head><body>";
		
		resHtml += "<H2>Risk Level:</H2>";
		resHtml += "Country: " + country + "<br />";
		resHtml += "Time classification: " + time + "<br />";
		
		// Only expect log in during work hours from the US
		if (country === "US" && time === "day")
			safe = true;
			
		// Log ons from China are expected at any time except weekends
		if (country === "CN" && time !== "weekend")
			safe = true;		
		
		if (safe)
			resHtml += '<span class="label label-pill label-success">' +
				'User name and password</span>';
		else
			resHtml += '<span class="label label-pill label-danger">' +
				'Two factor authentication</span>';
		
		resHtml += "</body></html>"
		
		res.send(resHtml);
	});	
});

To apply this policy, simply calculate the value of safe in
the login handler, and add an if statement for the next
step.

  if (safe) {
	createSession(user, res);
  } else {
 
	// Create request to confirm the logon
	var id = putRequest(req.body.email);
  
	// E-mail the account confirmation request
	sendLoginRequest(req.body.email, id);
  
	res.send("Thank you for your request. Please click the link you will receive by email to " +
	req.body.email + " shortly.");	
  }

Enhancements

There are several enhancements that can improve this program, making it
safer and more stable.

Keep safe from email
sniffers

There is a security problem, mentioned above, because any attacker who can
get the user’s email can break into the application by using the
confirmation link. One solution uses browser cookies. First, add
cookie-parser to package.json and use it in app.js:

// Use cookie-parser to read the cookies
var cookieParser = require("cookie-parser");
app.use(cookieParser());

Then, modify the login handler to:

  1. Generate a second random ID.
  2. Place that random ID in a browser cookie.
  3. Place the same random ID in the pending requests structure along with
    the user’s email address.

  // For preventing somebody who gets the email from logging on:
  var id2 = uuid.v4();    // 1  
  pendingReqs[id].cookie = id2;   // 2
  res.setHeader("Set-Cookie", ['secValue=' + id2]);  // 3

Also, modify the confirmation link handler to retrieve the value of the
cookie that is created in the login handler and compare that value to the
value in the pending request. If the values are not the same, the login
fails.

    // For preventing somebody who gets the email from logging on:
	if (req.cookies["secValue"] !== userRequest.cookie) {
		res.send("Wrong browser");
		return ;
	}

To verify that this works, log in from one device and then click the
confirmation email from another device, or from another browser on the
same device. This should fail.

Cleanup

Right now, if users do not click the link for some reason, the pending
request just stays active, taking up memory and increasing the time that
it takes to look up active requests.

To solve this, use the setInterval function to delete old
requests. JavaScript measures time in milliseconds, so to get 5 minutes it
is necessary to multiply 5 by 60,000.

// Delete old pending requests
var maxAge = 5*60*1000; // Delete requests older than five minutes

// Run this function every maxAge
setInterval(function() {
	var now = new Date();
	for (var id in pendingReqs) {   // For every pending request
		if (now - pendingReqs[id].time > maxAge)   // If it is old
			delete pendingReqs[id];   // Delete it
	}

Because the cleanup function runs every 5 minutes, pending requests are
deleted between 5 and 10 minutes after they are created.

}, maxAge);

Debugging

To debug the cleanup function, it is useful to know the value of
pendingReqs. This call makes it available from a browser.
(Note: Remember to delete this function before the
application is deployed in production. It discloses the two values that
can be used to break into the application.)

app.get("/pend", /* @callback */ function(req, res) {
	res.send(JSON.stringify(pendingReqs));
});

The /* @callback */ comment above does not change the
function. Its purpose is to tell the editor that even though
req is not used anywhere, it is required because it is a
callback function and you do not determine which parameters it gets. This
removes the warning and makes it easier to focus on potential
problems.

Screen shot showing parameter not used message

Screen shot showing parameter not used message

Require HTTPS

It is a bad idea to allow users to submit passwords and respond with
cookies in clear text. Add this call to redirect HTTP users to HTTPS. Put
it before any other handler declaration for the app.

//Handle all (any method) and any path (slash followed by any string)
app.all('/*', function(req, res, next) {

The application always gets HTTP, because the SSL tunnel is terminated by
IBM WebSphere® DataPower. However, the original protocol is available
in the header as x-forwarded-proto.

	// If the forwarded protocol isn't HTTPS, send a redirection
	if (req.headers["x-forwarded-proto"] !== "https")
		res.redirect("https://" + req.headers.host + req.path);
	else

The third parameter of the callback (for any part of the app.<HTTP
method>
functions, not just app.all), is the function to call
if this callback does not handle the request. If the request is already
HTTPS, you do not need to redirect and therefore you let normal processing
resume.

		next();
});

SMS instead of email

The Internet was not built with security in mind. The telephone network, on
the other hand, was. It is therefore safer to send tokens with SMS instead
of email. To do so:

  1. Add a mobile phone number field to the registration.
  2. Instead of a long token, create a short one that people can type. For
    example: uuid.v4().substring(0,5)
  3. Use the Twilio service in IBM Cloud to send the tokens by SMS.
  4. Instead of telling users to click the confirmation link, redirect them
    to a form where they can type the token.

User profiles

Instead of treating all users as identical, it is possible to store some
profile information, such as the user’s role or normal location, and
include that information in the risk analysis. For example, John Doe
typically logs in from the US. When he logs in from China, that might be
suspicious and require a second factor. But when Chang Xiu, an employee in
China, does it, it is not suspicious. The converse is true when Joe and
Chang both log in from the US, or when Chang logs in when it is noon
Central time, which would be 2 AM for him.

Conclusion

You should now be able to implement two-factor authentication in your
IBM Cloud Node.js applications. You should also be able to use risk analysis
to identify risky cases where it makes more sense to deploy two-factor
authentication.


Downloadable resources

Related topics

Credit: IBM

Previous Post

Telcos promise to stop selling location data, but Congress is not satisfied

Next Post

Jivox adds visual journey maps, site content to its resume

Related Posts

Six courses to build your technology skills in 2021 – IBM Developer
Technology Companies

How AI helps Overwatch League process 410M data points to build power rankings – IBM Developer

April 15, 2021
Six courses to build your technology skills in 2021 – IBM Developer
Technology Companies

A brief intro to Red Hat OpenShift for Node.js developers – IBM Developer

April 15, 2021
Six courses to build your technology skills in 2021 – IBM Developer
Technology Companies

IBM joins Eclipse Adoptium and offers free certified JDKs with Eclipse OpenJ9 – IBM Developer

April 14, 2021
Six courses to build your technology skills in 2021 – IBM Developer
Technology Companies

Every stroke matters – IBM Developer

April 14, 2021
Six courses to build your technology skills in 2021 – IBM Developer
Technology Companies

Day 1 inside the digital ops center – IBM Developer

April 10, 2021
Next Post
Jivox adds visual journey maps, site content to its resume

Jivox adds visual journey maps, site content to its resume

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recommended

Plasticity in Deep Learning: Dynamic Adaptations for AI Self-Driving Cars

Plasticity in Deep Learning: Dynamic Adaptations for AI Self-Driving Cars

January 6, 2019
Microsoft, Google Use Artificial Intelligence to Fight Hackers

Microsoft, Google Use Artificial Intelligence to Fight Hackers

January 6, 2019

Categories

  • Artificial Intelligence
  • Big Data
  • Blockchain
  • Crypto News
  • Data Science
  • Digital Marketing
  • Internet Privacy
  • Internet Security
  • Learn to Code
  • Machine Learning
  • Marketing Technology
  • Neural Networks
  • Technology Companies

Don't miss it

ML Scaling Requires Upgraded Data Management Plan
Machine Learning

ML Scaling Requires Upgraded Data Management Plan

April 17, 2021
SolarWinds cybersecurity spending tops $3 million in Q4, sees $20 million to $25 million in 2021
Internet Security

SolarWinds: US and UK blame Russian intelligence service hackers for major cyberattack

April 17, 2021
Machine learning can be your best bet to transform your career
Machine Learning

Machine learning can be your best bet to transform your career

April 17, 2021
AI and Human Rights, A Story About Equality | by bundleIQ | Mar, 2021
Neural Networks

AI and Human Rights, A Story About Equality | by bundleIQ | Mar, 2021

April 17, 2021
Monitor Your SEO Placement with SEObase
Learn to Code

Monitor Your SEO Placement with SEObase

April 17, 2021
Google Project Zero testing 30-day grace period on bug details to boost user patching
Internet Security

Google Project Zero testing 30-day grace period on bug details to boost user patching

April 17, 2021
NikolaNews

NikolaNews.com is an online News Portal which aims to share news about blockchain, AI, Big Data, and Data Privacy and more!

What’s New Here?

  • ML Scaling Requires Upgraded Data Management Plan April 17, 2021
  • SolarWinds: US and UK blame Russian intelligence service hackers for major cyberattack April 17, 2021
  • Machine learning can be your best bet to transform your career April 17, 2021
  • AI and Human Rights, A Story About Equality | by bundleIQ | Mar, 2021 April 17, 2021

Subscribe to get more!

© 2019 NikolaNews.com - Global Tech Updates

No Result
View All Result
  • AI Development
    • Artificial Intelligence
    • Machine Learning
    • Neural Networks
    • Learn to Code
  • Data
    • Blockchain
    • Big Data
    • Data Science
  • IT Security
    • Internet Privacy
    • Internet Security
  • Marketing
    • Digital Marketing
    • Marketing Technology
  • Technology Companies
  • Crypto News

© 2019 NikolaNews.com - Global Tech Updates